mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-01-27 07:32:20 +01:00
2133 lines
60 KiB
C
2133 lines
60 KiB
C
/* ocsp.c
|
|
*
|
|
* Copyright (C) 2006-2025 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
|
|
|
|
/* Name change compatibility layer no longer needs to be included here */
|
|
|
|
/*
|
|
* WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK:
|
|
* Disable looking for an authorized responder in the verification path of
|
|
* the issuer. This will make the authorized responder only look at the
|
|
* OCSP response signer and direct issuer.
|
|
*/
|
|
|
|
#ifndef WOLFCRYPT_ONLY
|
|
#ifdef HAVE_OCSP
|
|
|
|
#include <wolfssl/error-ssl.h>
|
|
#include <wolfssl/ocsp.h>
|
|
#include <wolfssl/internal.h>
|
|
|
|
#ifdef NO_INLINE
|
|
#include <wolfssl/wolfcrypt/misc.h>
|
|
#else
|
|
#define WOLFSSL_MISC_INCLUDED
|
|
#include <wolfcrypt/src/misc.c>
|
|
#endif
|
|
|
|
/* Allocates and initializes a WOLFSSL_OCSP object. Returns pointer on success, NULL on failure. */
|
|
WOLFSSL_OCSP* wc_NewOCSP(WOLFSSL_CERT_MANAGER* cm)
|
|
{
|
|
WOLFSSL_OCSP* ocsp = NULL;
|
|
ocsp = (WOLFSSL_OCSP*)XMALLOC(sizeof(WOLFSSL_OCSP), cm ? cm->heap : NULL, DYNAMIC_TYPE_OCSP);
|
|
if (ocsp == NULL)
|
|
return NULL;
|
|
if (InitOCSP(ocsp, cm) != 0) {
|
|
XFREE(ocsp, cm ? cm->heap : NULL, DYNAMIC_TYPE_OCSP);
|
|
return NULL;
|
|
}
|
|
return ocsp;
|
|
}
|
|
|
|
/* Frees a WOLFSSL_OCSP object allocated by wc_NewOCSP. */
|
|
void wc_FreeOCSP(WOLFSSL_OCSP* ocsp)
|
|
{
|
|
if (ocsp) {
|
|
FreeOCSP(ocsp, 1);
|
|
}
|
|
}
|
|
|
|
int wc_CheckCertOcspResponse(WOLFSSL_OCSP *ocsp, DecodedCert *cert,
|
|
byte *response, int responseSz, void* heap)
|
|
{
|
|
int ret = WC_NO_ERR_TRACE(ASN_OCSP_CONFIRM_E);
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
OcspRequest* ocspRequest;
|
|
#else
|
|
OcspRequest ocspRequest[1];
|
|
#endif
|
|
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
ocspRequest = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (ocspRequest == NULL) {
|
|
WOLFSSL_LEAVE("CheckCertOCSP", MEMORY_ERROR);
|
|
return MEMORY_E;
|
|
}
|
|
#endif
|
|
|
|
if (InitOcspRequest(ocspRequest, cert, ocsp->cm->ocspSendNonce,
|
|
ocsp->cm->heap) == 0) {
|
|
ret = CheckOcspResponse(ocsp, response, responseSz, NULL, NULL, NULL,
|
|
ocspRequest, heap);
|
|
FreeOcspRequest(ocspRequest);
|
|
}
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
XFREE(ocspRequest, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
|
|
WOLFSSL_LEAVE("CheckCertOCSP", ret);
|
|
return ret;
|
|
}
|
|
|
|
int InitOCSP(WOLFSSL_OCSP* ocsp, WOLFSSL_CERT_MANAGER* cm)
|
|
{
|
|
WOLFSSL_ENTER("InitOCSP");
|
|
|
|
ForceZero(ocsp, sizeof(WOLFSSL_OCSP));
|
|
|
|
if (wc_InitMutex(&ocsp->ocspLock) != 0)
|
|
return BAD_MUTEX_E;
|
|
|
|
ocsp->cm = cm;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int InitOcspEntry(OcspEntry* entry, OcspRequest* request)
|
|
{
|
|
WOLFSSL_ENTER("InitOcspEntry");
|
|
|
|
ForceZero(entry, sizeof(OcspEntry));
|
|
|
|
XMEMCPY(entry->issuerHash, request->issuerHash, OCSP_DIGEST_SIZE);
|
|
XMEMCPY(entry->issuerKeyHash, request->issuerKeyHash, OCSP_DIGEST_SIZE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void FreeOcspEntry(OcspEntry* entry, void* heap)
|
|
{
|
|
CertStatus *status, *next;
|
|
|
|
if (entry == NULL || !entry->ownStatus)
|
|
return;
|
|
|
|
WOLFSSL_ENTER("FreeOcspEntry");
|
|
|
|
for (status = entry->status; status; status = next) {
|
|
next = status->next;
|
|
|
|
XFREE(status->rawOcspResponse, heap, DYNAMIC_TYPE_OCSP_STATUS);
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
if (status->serialInt) {
|
|
if (status->serialInt->isDynamic) {
|
|
XFREE(status->serialInt->data, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
}
|
|
XFREE(status->serialInt, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
}
|
|
status->serialInt = NULL;
|
|
#endif
|
|
|
|
XFREE(status, heap, DYNAMIC_TYPE_OCSP_STATUS);
|
|
}
|
|
|
|
(void)heap;
|
|
}
|
|
|
|
|
|
void FreeOCSP(WOLFSSL_OCSP* ocsp, int dynamic)
|
|
{
|
|
OcspEntry *entry, *next;
|
|
|
|
WOLFSSL_ENTER("FreeOCSP");
|
|
|
|
for (entry = ocsp->ocspList; entry; entry = next) {
|
|
next = entry->next;
|
|
FreeOcspEntry(entry, ocsp->cm->heap);
|
|
XFREE(entry, ocsp->cm->heap, DYNAMIC_TYPE_OCSP_ENTRY);
|
|
}
|
|
|
|
wc_FreeMutex(&ocsp->ocspLock);
|
|
|
|
if (dynamic)
|
|
XFREE(ocsp, ocsp->cm->heap, DYNAMIC_TYPE_OCSP);
|
|
|
|
}
|
|
|
|
|
|
static int xstat2err(int st)
|
|
{
|
|
switch (st) {
|
|
case CERT_GOOD:
|
|
return 0;
|
|
case CERT_REVOKED:
|
|
return OCSP_CERT_REVOKED;
|
|
default:
|
|
return OCSP_CERT_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
int CheckCertOCSP_ex(WOLFSSL_OCSP* ocsp, DecodedCert* cert, WOLFSSL* ssl)
|
|
{
|
|
int ret = WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL);
|
|
|
|
WC_DECLARE_VAR(ocspRequest, OcspRequest, 1, 0);
|
|
|
|
WOLFSSL_ENTER("CheckCertOCSP");
|
|
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
ocspRequest = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (ocspRequest == NULL) {
|
|
WOLFSSL_LEAVE("CheckCertOCSP", MEMORY_ERROR);
|
|
return MEMORY_E;
|
|
}
|
|
#endif
|
|
|
|
if (InitOcspRequest(ocspRequest, cert, ocsp->cm->ocspSendNonce,
|
|
ocsp->cm->heap) == 0) {
|
|
ocspRequest->ssl = ssl;
|
|
ret = CheckOcspRequest(ocsp, ocspRequest, NULL, NULL);
|
|
|
|
FreeOcspRequest(ocspRequest);
|
|
}
|
|
|
|
WC_FREE_VAR_EX(ocspRequest, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
WOLFSSL_LEAVE("CheckCertOCSP", ret);
|
|
return ret;
|
|
}
|
|
int CheckCertOCSP(WOLFSSL_OCSP* ocsp, DecodedCert* cert)
|
|
{
|
|
return CheckCertOCSP_ex(ocsp, cert, NULL);
|
|
}
|
|
|
|
static int GetOcspEntry(WOLFSSL_OCSP* ocsp, OcspRequest* request,
|
|
OcspEntry** entry)
|
|
{
|
|
WOLFSSL_ENTER("GetOcspEntry");
|
|
|
|
*entry = NULL;
|
|
|
|
if (wc_LockMutex(&ocsp->ocspLock) != 0) {
|
|
WOLFSSL_LEAVE("CheckCertOCSP", BAD_MUTEX_E);
|
|
return BAD_MUTEX_E;
|
|
}
|
|
|
|
for (*entry = ocsp->ocspList; *entry; *entry = (*entry)->next)
|
|
if (XMEMCMP((*entry)->issuerHash, request->issuerHash,
|
|
OCSP_DIGEST_SIZE) == 0
|
|
&& XMEMCMP((*entry)->issuerKeyHash, request->issuerKeyHash,
|
|
OCSP_DIGEST_SIZE) == 0)
|
|
break;
|
|
|
|
if (*entry == NULL) {
|
|
*entry = (OcspEntry*)XMALLOC(sizeof(OcspEntry),
|
|
ocsp->cm->heap, DYNAMIC_TYPE_OCSP_ENTRY);
|
|
if (*entry) {
|
|
InitOcspEntry(*entry, request);
|
|
(*entry)->next = ocsp->ocspList;
|
|
ocsp->ocspList = *entry;
|
|
}
|
|
}
|
|
|
|
wc_UnLockMutex(&ocsp->ocspLock);
|
|
|
|
return *entry ? 0 : MEMORY_ERROR;
|
|
}
|
|
|
|
|
|
/* Mallocs responseBuffer->buffer and is up to caller to free on success
|
|
*
|
|
* Returns OCSP status
|
|
*/
|
|
static int GetOcspStatus(WOLFSSL_OCSP* ocsp, OcspRequest* request,
|
|
OcspEntry* entry, CertStatus** status, buffer* responseBuffer,
|
|
void* heap)
|
|
{
|
|
int ret = WC_NO_ERR_TRACE(OCSP_INVALID_STATUS);
|
|
|
|
WOLFSSL_ENTER("GetOcspStatus");
|
|
|
|
(void)heap;
|
|
*status = NULL;
|
|
|
|
if (wc_LockMutex(&ocsp->ocspLock) != 0) {
|
|
WOLFSSL_LEAVE("CheckCertOCSP", BAD_MUTEX_E);
|
|
return BAD_MUTEX_E;
|
|
}
|
|
|
|
for (*status = entry->status; *status; *status = (*status)->next)
|
|
if ((*status)->serialSz == request->serialSz
|
|
&& !XMEMCMP((*status)->serial, request->serial, (size_t)(*status)->serialSz))
|
|
break;
|
|
|
|
if (responseBuffer && *status && !(*status)->rawOcspResponse) {
|
|
/* force fetching again */
|
|
ret = OCSP_INVALID_STATUS;
|
|
}
|
|
else if (*status) {
|
|
#ifndef NO_ASN_TIME
|
|
if (XVALIDATE_DATE((*status)->thisDate,
|
|
(*status)->thisDateFormat, ASN_BEFORE)
|
|
&& ((*status)->nextDate[0] != 0)
|
|
&& XVALIDATE_DATE((*status)->nextDate,
|
|
(*status)->nextDateFormat, ASN_AFTER))
|
|
#endif
|
|
{
|
|
ret = xstat2err((*status)->status);
|
|
|
|
if (responseBuffer) {
|
|
responseBuffer->buffer = (byte*)XMALLOC(
|
|
(*status)->rawOcspResponseSz, heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
if (responseBuffer->buffer) {
|
|
responseBuffer->length = (*status)->rawOcspResponseSz;
|
|
XMEMCPY(responseBuffer->buffer,
|
|
(*status)->rawOcspResponse,
|
|
(*status)->rawOcspResponseSz);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
wc_UnLockMutex(&ocsp->ocspLock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Check that the response for validity. Store result in status.
|
|
*
|
|
* ocsp Context object for OCSP status.
|
|
* response OCSP response message data.
|
|
* responseSz Length of OCSP response message data.
|
|
* responseBuffer Buffer object to return the response with.
|
|
* status The certificate status object.
|
|
* entry The OCSP entry for this certificate.
|
|
* ocspRequest Request corresponding to response.
|
|
* heap Heap hint used for responseBuffer
|
|
* returns OCSP_LOOKUP_FAIL when the response is bad and 0 otherwise.
|
|
*/
|
|
int CheckOcspResponse(WOLFSSL_OCSP *ocsp, byte *response, int responseSz,
|
|
WOLFSSL_BUFFER_INFO *responseBuffer, CertStatus *status,
|
|
OcspEntry *entry, OcspRequest *ocspRequest, void* heap)
|
|
{
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
CertStatus* newStatus;
|
|
OcspEntry* newSingle;
|
|
OcspResponse* ocspResponse;
|
|
#else
|
|
CertStatus newStatus[1];
|
|
OcspEntry newSingle[1];
|
|
OcspResponse ocspResponse[1];
|
|
#endif
|
|
int ret;
|
|
int validated = 0; /* ocsp validation flag */
|
|
|
|
(void)heap;
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
newStatus = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL,
|
|
DYNAMIC_TYPE_OCSP_STATUS);
|
|
newSingle = (OcspEntry*)XMALLOC(sizeof(OcspEntry), NULL,
|
|
DYNAMIC_TYPE_OCSP_ENTRY);
|
|
ocspResponse = (OcspResponse*)XMALLOC(sizeof(OcspResponse), NULL,
|
|
DYNAMIC_TYPE_OCSP_REQUEST);
|
|
|
|
if (newStatus == NULL || newSingle == NULL || ocspResponse == NULL) {
|
|
XFREE(newStatus, NULL, DYNAMIC_TYPE_OCSP_STATUS);
|
|
XFREE(newSingle, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
|
|
XFREE(ocspResponse, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
|
|
|
|
WOLFSSL_LEAVE("CheckCertOCSP", MEMORY_ERROR);
|
|
return MEMORY_E;
|
|
}
|
|
#endif
|
|
InitOcspResponse(ocspResponse, newSingle, newStatus, response,
|
|
(word32)responseSz, ocsp->cm->heap);
|
|
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
|
|
if (ocspRequest != NULL && ocspRequest->ssl != NULL &&
|
|
TLSX_CSR2_IsMulti(((WOLFSSL*)ocspRequest->ssl)->extensions)) {
|
|
ocspResponse->pendingCAs = TLSX_CSR2_GetPendingSigners(((WOLFSSL*)ocspRequest->ssl)->extensions);
|
|
}
|
|
#endif
|
|
ret = OcspResponseDecode(ocspResponse, ocsp->cm, ocsp->cm->heap, 0, 0);
|
|
if (ret != 0) {
|
|
ocsp->error = ret;
|
|
WOLFSSL_LEAVE("OcspResponseDecode failed", ocsp->error);
|
|
goto end;
|
|
}
|
|
|
|
if (ocspResponse->responseStatus != OCSP_SUCCESSFUL) {
|
|
WOLFSSL_MSG("OcspResponse status bad");
|
|
goto end;
|
|
}
|
|
if (ocspRequest != NULL) {
|
|
/* Has the chance to bubble up response changing ocspResponse->single to
|
|
no longer be pointing at newSingle */
|
|
ret = CompareOcspReqResp(ocspRequest, ocspResponse);
|
|
if (ret != 0) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
if (responseBuffer) {
|
|
responseBuffer->buffer = (byte*)XMALLOC((size_t)responseSz, heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
if (responseBuffer->buffer) {
|
|
responseBuffer->length = (unsigned int)responseSz;
|
|
XMEMCPY(responseBuffer->buffer, response, (size_t)responseSz);
|
|
}
|
|
}
|
|
|
|
ret = xstat2err(ocspResponse->single->status->status);
|
|
if (ret == 0) {
|
|
validated = 1;
|
|
}
|
|
|
|
if (wc_LockMutex(&ocsp->ocspLock) != 0) {
|
|
ret = BAD_MUTEX_E;
|
|
goto end;
|
|
}
|
|
|
|
if (status != NULL) {
|
|
XFREE(status->rawOcspResponse, ocsp->cm->heap, DYNAMIC_TYPE_OCSP_STATUS);
|
|
|
|
/* Replace existing certificate entry with updated */
|
|
ocspResponse->single->status->next = status->next;
|
|
XMEMCPY(status, ocspResponse->single->status, sizeof(CertStatus));
|
|
}
|
|
else if (entry != NULL) {
|
|
/* Save new certificate entry */
|
|
status = (CertStatus*)XMALLOC(sizeof(CertStatus),
|
|
ocsp->cm->heap, DYNAMIC_TYPE_OCSP_STATUS);
|
|
if (status != NULL) {
|
|
XMEMCPY(status, ocspResponse->single->status, sizeof(CertStatus));
|
|
status->next = entry->status;
|
|
entry->status = status;
|
|
entry->ownStatus = 1;
|
|
entry->totalStatus++;
|
|
}
|
|
}
|
|
|
|
if (status && responseBuffer && responseBuffer->buffer) {
|
|
status->rawOcspResponse = (byte*)XMALLOC(responseBuffer->length,
|
|
ocsp->cm->heap,
|
|
DYNAMIC_TYPE_OCSP_STATUS);
|
|
|
|
if (status->rawOcspResponse) {
|
|
status->rawOcspResponseSz = responseBuffer->length;
|
|
XMEMCPY(status->rawOcspResponse, responseBuffer->buffer,
|
|
responseBuffer->length);
|
|
}
|
|
}
|
|
|
|
wc_UnLockMutex(&ocsp->ocspLock);
|
|
|
|
end:
|
|
if (ret == 0 && validated == 1) {
|
|
WOLFSSL_MSG("New OcspResponse validated");
|
|
}
|
|
else if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED)) {
|
|
WOLFSSL_MSG("OCSP revoked");
|
|
}
|
|
else if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN)) {
|
|
WOLFSSL_MSG("OCSP unknown");
|
|
}
|
|
else {
|
|
WOLFSSL_MSG("OCSP lookup failure");
|
|
ret = OCSP_LOOKUP_FAIL;
|
|
}
|
|
|
|
FreeOcspResponse(ocspResponse);
|
|
WC_FREE_VAR_EX(newStatus, NULL, DYNAMIC_TYPE_OCSP_STATUS);
|
|
WC_FREE_VAR_EX(newSingle, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
|
|
WC_FREE_VAR_EX(ocspResponse, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
|
|
return ret;
|
|
}
|
|
|
|
/* 0 on success */
|
|
/* allow user to override the maximum request size at build-time */
|
|
#ifndef OCSP_MAX_REQUEST_SZ
|
|
#define OCSP_MAX_REQUEST_SZ 2048
|
|
#endif
|
|
int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest,
|
|
buffer* responseBuffer, void* heap)
|
|
{
|
|
OcspEntry* entry = NULL;
|
|
CertStatus* status = NULL;
|
|
byte* request = NULL;
|
|
int requestSz = OCSP_MAX_REQUEST_SZ;
|
|
int responseSz = 0;
|
|
byte* response = NULL;
|
|
const char* url = NULL;
|
|
int urlSz = 0;
|
|
int ret = -1;
|
|
WOLFSSL* ssl;
|
|
void* ioCtx;
|
|
|
|
WOLFSSL_ENTER("CheckOcspRequest");
|
|
|
|
if (ocsp == NULL || ocspRequest == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
if (responseBuffer) {
|
|
responseBuffer->buffer = NULL;
|
|
responseBuffer->length = 0;
|
|
}
|
|
|
|
ret = GetOcspEntry(ocsp, ocspRequest, &entry);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = GetOcspStatus(ocsp, ocspRequest, entry, &status, responseBuffer,
|
|
heap);
|
|
if (ret != WC_NO_ERR_TRACE(OCSP_INVALID_STATUS))
|
|
return ret;
|
|
|
|
if (responseBuffer) {
|
|
XFREE(responseBuffer->buffer, heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
responseBuffer->buffer = NULL;
|
|
}
|
|
|
|
/* get SSL and IOCtx */
|
|
ssl = (WOLFSSL*)ocspRequest->ssl;
|
|
ioCtx = (ssl && ssl->ocspIOCtx != NULL) ?
|
|
ssl->ocspIOCtx : ocsp->cm->ocspIOCtx;
|
|
|
|
if (ocsp->cm->ocspUseOverrideURL) {
|
|
url = ocsp->cm->ocspOverrideURL;
|
|
if (url != NULL && url[0] != '\0')
|
|
urlSz = (int)XSTRLEN(url);
|
|
else
|
|
return OCSP_NEED_URL;
|
|
}
|
|
else if (ocspRequest->urlSz != 0 && ocspRequest->url != NULL) {
|
|
url = (const char *)ocspRequest->url;
|
|
urlSz = ocspRequest->urlSz;
|
|
}
|
|
else {
|
|
/* cert doesn't have extAuthInfo, assuming CERT_GOOD */
|
|
WOLFSSL_MSG("Cert has no OCSP URL, assuming CERT_GOOD");
|
|
return 0;
|
|
}
|
|
|
|
request = (byte*)XMALLOC((size_t)requestSz, ocsp->cm->heap, DYNAMIC_TYPE_OCSP);
|
|
if (request == NULL) {
|
|
WOLFSSL_LEAVE("CheckCertOCSP", MEMORY_ERROR);
|
|
return MEMORY_ERROR;
|
|
}
|
|
|
|
requestSz = EncodeOcspRequest(ocspRequest, request, (word32)requestSz);
|
|
if (requestSz > 0 && ocsp->cm->ocspIOCb) {
|
|
responseSz = ocsp->cm->ocspIOCb(ioCtx, url, urlSz,
|
|
request, requestSz, &response);
|
|
}
|
|
if (responseSz == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ)) {
|
|
ret = OCSP_WANT_READ;
|
|
}
|
|
else if (responseSz == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_TIMEOUT)){
|
|
ret = HTTP_TIMEOUT;
|
|
}
|
|
|
|
XFREE(request, ocsp->cm->heap, DYNAMIC_TYPE_OCSP);
|
|
|
|
if (responseSz >= 0 && response) {
|
|
ret = CheckOcspResponse(ocsp, response, responseSz, responseBuffer, status,
|
|
entry, ocspRequest, heap);
|
|
}
|
|
|
|
if (response != NULL && ocsp->cm->ocspRespFreeCb)
|
|
ocsp->cm->ocspRespFreeCb(ioCtx, response);
|
|
|
|
/* Keep responseBuffer in the case of getting to response check. Caller
|
|
* should free responseBuffer after checking OCSP return value in "ret" */
|
|
WOLFSSL_LEAVE("CheckOcspRequest", ret);
|
|
return ret;
|
|
}
|
|
|
|
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK
|
|
static int CheckOcspResponderChain(OcspEntry* single, DecodedCert *cert,
|
|
void* vp, Signer* pendingCAs) {
|
|
/* Attempt to build a chain up to cert's issuer */
|
|
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp;
|
|
Signer* ca = NULL;
|
|
Signer* prev = NULL;
|
|
int passed = 0;
|
|
|
|
/*
|
|
* Relation between certs:
|
|
* CA
|
|
* / \
|
|
* intermediate(s) cert in OCSP response
|
|
* | with OCSP key usage ext
|
|
* issuer of cert
|
|
* in OCSP request
|
|
*/
|
|
|
|
/* End loop if no more issuers found or if we have found a self
|
|
* signed cert (ca == prev) */
|
|
ca = GetCAByName(cm, single->issuerHash);
|
|
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
|
|
if (ca == NULL && pendingCAs != NULL) {
|
|
ca = findSignerByName(pendingCAs, single->issuerHash);
|
|
}
|
|
#else
|
|
(void)pendingCAs;
|
|
#endif
|
|
for (; ca != NULL && ca != prev;
|
|
prev = ca) {
|
|
if (XMEMCMP(cert->issuerHash, ca->issuerNameHash,
|
|
OCSP_DIGEST_SIZE) == 0) {
|
|
WOLFSSL_MSG("\tOCSP Response signed by authorized "
|
|
"responder delegated by issuer "
|
|
"(found in chain)");
|
|
passed = 1;
|
|
break;
|
|
}
|
|
ca = GetCAByName(cm, ca->issuerNameHash);
|
|
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
|
|
if (ca == NULL && pendingCAs != NULL) {
|
|
ca = findSignerByName(pendingCAs, single->issuerHash);
|
|
}
|
|
#endif
|
|
}
|
|
return passed;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Enforce https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2
|
|
* @param bs The basic response to verify
|
|
* @param cert The decoded bs->cert
|
|
* @return
|
|
*/
|
|
int CheckOcspResponder(OcspResponse *bs, DecodedCert *cert, void* vp)
|
|
{
|
|
int ret = 0;
|
|
OcspEntry* single;
|
|
|
|
/* Both evaluate to enum values so can't use a pre-processor check */
|
|
WOLFSSL_ASSERT_EQ(OCSP_DIGEST_SIZE, SIGNER_DIGEST_SIZE);
|
|
|
|
(void)vp;
|
|
|
|
WOLFSSL_ENTER("CheckOcspResponder");
|
|
|
|
/* In the future if this API is used more then it could be beneficial to
|
|
* implement calling InitDecodedCert and ParseCertRelative here
|
|
* automatically when cert == NULL. */
|
|
if (bs == NULL || cert == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
/* Traverse the list and check that the cert has the authority to provide
|
|
* an OCSP response for each entry. */
|
|
for (single = bs->single; single != NULL; single = single->next) {
|
|
int passed = 0;
|
|
|
|
if (XMEMCMP(cert->subjectHash, single->issuerHash, OCSP_DIGEST_SIZE)
|
|
== 0) {
|
|
WOLFSSL_MSG("\tOCSP Response signed by issuer");
|
|
passed = 1;
|
|
}
|
|
else if ((cert->extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) != 0) {
|
|
if (XMEMCMP(cert->issuerHash, single->issuerHash,
|
|
OCSP_DIGEST_SIZE) == 0) {
|
|
WOLFSSL_MSG("\tOCSP Response signed by authorized responder "
|
|
"delegated by issuer");
|
|
passed = 1;
|
|
}
|
|
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK
|
|
else if (vp != NULL) {
|
|
passed = CheckOcspResponderChain(single, cert, vp, bs->pendingCAs);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (!passed) {
|
|
WOLFSSL_MSG("\tOCSP Responder not authorized");
|
|
ret = BAD_OCSP_RESPONDER;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* compatibility layer OCSP functions */
|
|
#ifdef OPENSSL_EXTRA
|
|
int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs,
|
|
WOLFSSL_OCSP_CERTID* id, int* status, int* reason,
|
|
WOLFSSL_ASN1_TIME** revtime, WOLFSSL_ASN1_TIME** thisupd,
|
|
WOLFSSL_ASN1_TIME** nextupd)
|
|
{
|
|
WOLFSSL_OCSP_SINGLERESP* single;
|
|
|
|
if (bs == NULL || id == NULL)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
single = bs->single;
|
|
while (single != NULL) {
|
|
if ((XMEMCMP(single->status->serial, id->status->serial, (size_t)single->status->serialSz) == 0)
|
|
&& (XMEMCMP(single->issuerHash, id->issuerHash, OCSP_DIGEST_SIZE) == 0)
|
|
&& (XMEMCMP(single->issuerKeyHash, id->issuerKeyHash, OCSP_DIGEST_SIZE) == 0)) {
|
|
break;
|
|
}
|
|
single = single->next;
|
|
}
|
|
|
|
if (single == NULL)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
if (status != NULL)
|
|
*status = single->status->status;
|
|
#ifdef WOLFSSL_OCSP_PARSE_STATUS
|
|
if (thisupd != NULL)
|
|
*thisupd = &single->status->thisDateParsed;
|
|
if (nextupd != NULL)
|
|
*nextupd = &single->status->nextDateParsed;
|
|
#else
|
|
if (thisupd != NULL)
|
|
*thisupd = NULL;
|
|
if (nextupd != NULL)
|
|
*nextupd = NULL;
|
|
#endif
|
|
|
|
/* TODO: Not needed for Nginx or httpd */
|
|
if (reason != NULL)
|
|
*reason = 0;
|
|
if (revtime != NULL)
|
|
*revtime = NULL;
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
const char *wolfSSL_OCSP_cert_status_str(long s)
|
|
{
|
|
switch (s) {
|
|
case CERT_GOOD:
|
|
return "good";
|
|
case CERT_REVOKED:
|
|
return "revoked";
|
|
case CERT_UNKNOWN:
|
|
return "unknown";
|
|
default:
|
|
return "(UNKNOWN)";
|
|
}
|
|
}
|
|
|
|
int wolfSSL_OCSP_check_validity(WOLFSSL_ASN1_TIME* thisupd,
|
|
WOLFSSL_ASN1_TIME* nextupd, long sec, long maxsec)
|
|
{
|
|
(void)thisupd;
|
|
(void)nextupd;
|
|
(void)sec;
|
|
(void)maxsec;
|
|
/* Dates validated in DecodeSingleResponse. */
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
void wolfSSL_OCSP_CERTID_free(WOLFSSL_OCSP_CERTID* certId)
|
|
{
|
|
FreeOcspEntry(certId, NULL);
|
|
XFREE(certId, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
}
|
|
|
|
WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_cert_to_id(
|
|
const WOLFSSL_EVP_MD *dgst, const WOLFSSL_X509 *subject,
|
|
const WOLFSSL_X509 *issuer)
|
|
{
|
|
WOLFSSL_OCSP_CERTID* certId = NULL;
|
|
CertStatus* certStatus = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = NULL;
|
|
int ret = -1;
|
|
DerBuffer* derCert = NULL;
|
|
int dgstType;
|
|
WC_DECLARE_VAR(cert, DecodedCert, 1, 0);
|
|
|
|
if (dgst == NULL) {
|
|
dgstType = WC_HASH_TYPE_SHA;
|
|
}
|
|
else if (wolfSSL_EVP_get_hashinfo(dgst, &dgstType, NULL) !=
|
|
WOLFSSL_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
|
|
if (dgstType != OCSP_DIGEST)
|
|
return NULL;
|
|
|
|
cm = wolfSSL_CertManagerNew();
|
|
if (cm == NULL
|
|
|| subject == NULL || subject->derCert == NULL
|
|
|| issuer == NULL || issuer->derCert == NULL)
|
|
goto out;
|
|
|
|
WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT,
|
|
goto out);
|
|
|
|
ret = AllocDer(&derCert, issuer->derCert->length,
|
|
issuer->derCert->type, NULL);
|
|
if (ret == 0) {
|
|
/* AddCA() frees the buffer. */
|
|
XMEMCPY(derCert->buffer, issuer->derCert->buffer,
|
|
issuer->derCert->length);
|
|
ret = AddCA(cm, &derCert, WOLFSSL_USER_CA, 1);
|
|
if (ret != WOLFSSL_SUCCESS) {
|
|
goto out;
|
|
}
|
|
derCert = NULL;
|
|
}
|
|
|
|
ret = -1;
|
|
|
|
certId = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID),
|
|
cm->heap, DYNAMIC_TYPE_OPENSSL);
|
|
if (certId == NULL)
|
|
goto out;
|
|
|
|
certStatus = (CertStatus*)XMALLOC(sizeof(CertStatus), cm->heap,
|
|
DYNAMIC_TYPE_OPENSSL);
|
|
if (certStatus == NULL)
|
|
goto out;
|
|
|
|
XMEMSET(certId, 0, sizeof(WOLFSSL_OCSP_CERTID));
|
|
XMEMSET(certStatus, 0, sizeof(CertStatus));
|
|
|
|
certId->status = certStatus;
|
|
certId->ownStatus = 1;
|
|
|
|
InitDecodedCert(cert, subject->derCert->buffer,
|
|
subject->derCert->length, NULL);
|
|
if (ParseCertRelative(cert, CERT_TYPE, VERIFY_OCSP, cm, NULL) != 0) {
|
|
FreeDecodedCert(cert);
|
|
goto out;
|
|
}
|
|
else {
|
|
certId->hashAlgoOID = wc_HashGetOID(OCSP_DIGEST);
|
|
XMEMCPY(certId->issuerHash, cert->issuerHash, OCSP_DIGEST_SIZE);
|
|
XMEMCPY(certId->issuerKeyHash, cert->issuerKeyHash, OCSP_DIGEST_SIZE);
|
|
XMEMCPY(certId->status->serial, cert->serial, (size_t)cert->serialSz);
|
|
certId->status->serialSz = cert->serialSz;
|
|
FreeDecodedCert(cert);
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
if (ret != 0) {
|
|
if (derCert != NULL)
|
|
FreeDer(&derCert);
|
|
if (cm != NULL) {
|
|
XFREE(certId, cm->heap, DYNAMIC_TYPE_OPENSSL);
|
|
certId = NULL;
|
|
XFREE(certStatus, cm->heap, DYNAMIC_TYPE_OPENSSL);
|
|
}
|
|
}
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
if (cm != NULL)
|
|
XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT);
|
|
#endif
|
|
|
|
if (cm != NULL)
|
|
wolfSSL_CertManagerFree(cm);
|
|
|
|
return certId;
|
|
}
|
|
|
|
void wolfSSL_OCSP_BASICRESP_free(WOLFSSL_OCSP_BASICRESP* basicResponse)
|
|
{
|
|
wolfSSL_OCSP_RESPONSE_free(basicResponse);
|
|
}
|
|
|
|
/* Calculate ancode CertID DER encoding following RFC 6960:
|
|
CertID ::= SEQUENCE {
|
|
hashAlgorithm AlgorithmIdentifier,
|
|
issuerNameHash OCTET STRING,
|
|
issuerKeyHash OCTET STRING,
|
|
serialNumber CertificateSerialNumber }
|
|
*/
|
|
static int OcspEncodeCertID(WOLFSSL_OCSP_CERTID* id, byte* output,
|
|
word32* totalSz, word32* intSize)
|
|
{
|
|
word32 idx = 0;
|
|
int ret;
|
|
|
|
if (id == NULL || totalSz == NULL || intSize == NULL ||
|
|
(output != NULL && (*totalSz == 0 || *totalSz <= *intSize)))
|
|
return BAD_FUNC_ARG;
|
|
|
|
if (output != NULL) {
|
|
ret = SetSequence(*intSize, output);
|
|
if (ret < 0)
|
|
return ret;
|
|
idx += ret;
|
|
}
|
|
|
|
ret = SetAlgoID(id->hashAlgoOID, ((output != NULL) ? output + idx : output),
|
|
oidHashType, 0);
|
|
if (ret <= 0)
|
|
return -1;
|
|
idx += ret;
|
|
|
|
/* issuerNameHash */
|
|
ret = SetOctetString(OCSP_DIGEST_SIZE, ((output != NULL) ? output + idx : output));
|
|
if (ret < 0)
|
|
return ret;
|
|
idx += ret;
|
|
if (output != NULL)
|
|
XMEMCPY(output + idx, id->issuerHash, OCSP_DIGEST_SIZE);
|
|
idx += OCSP_DIGEST_SIZE;
|
|
|
|
/* issuerKeyHash */
|
|
ret = SetOctetString(OCSP_DIGEST_SIZE, ((output != NULL) ? output + idx : output));
|
|
if (ret < 0)
|
|
return ret;
|
|
idx += ret;
|
|
if (output != NULL)
|
|
XMEMCPY(output + idx, id->issuerKeyHash, OCSP_DIGEST_SIZE);
|
|
idx += OCSP_DIGEST_SIZE;
|
|
|
|
/* serialNumber */
|
|
ret = SetASNInt(id->status->serialSz, id->status->serial[0], ((output != NULL) ? output + idx : output));
|
|
if (ret < 0)
|
|
return ret;
|
|
idx += ret;
|
|
if (output != NULL)
|
|
XMEMCPY(output + idx, id->status->serial, id->status->serialSz);
|
|
idx += id->status->serialSz;
|
|
|
|
if (output == NULL) {
|
|
*intSize = idx;
|
|
ret = SetSequence(idx, NULL);
|
|
if (ret < 0)
|
|
return ret;
|
|
idx += ret;
|
|
*totalSz = idx;
|
|
}
|
|
else if (idx != *totalSz) {
|
|
return BUFFER_E;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int OcspRespIdMatches(OcspResponse* resp, const byte* NameHash,
|
|
const byte* keyHash)
|
|
{
|
|
if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) {
|
|
return XMEMCMP(NameHash, resp->responderId.nameHash,
|
|
SIGNER_DIGEST_SIZE) == 0;
|
|
}
|
|
else if (resp->responderIdType == OCSP_RESPONDER_ID_KEY) {
|
|
return XMEMCMP(keyHash, resp->responderId.keyHash, KEYID_SIZE) == 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int OcspFindSigner(WOLFSSL_OCSP_BASICRESP *resp,
|
|
WOLF_STACK_OF(WOLFSSL_X509) *certs, DecodedCert **signer, int *embedded,
|
|
unsigned long flags)
|
|
{
|
|
WOLFSSL_X509 *signer_x509 = NULL;
|
|
DecodedCert *certDecoded;
|
|
int i;
|
|
|
|
certDecoded = (DecodedCert *)XMALLOC(sizeof(*certDecoded), resp->heap,
|
|
DYNAMIC_TYPE_DCERT);
|
|
if (certDecoded == NULL)
|
|
return MEMORY_E;
|
|
|
|
for (i = 0; i < wolfSSL_sk_X509_num(certs); i++) {
|
|
signer_x509 = wolfSSL_sk_X509_value(certs, i);
|
|
if (signer_x509 == NULL)
|
|
continue;
|
|
|
|
InitDecodedCert(certDecoded, signer_x509->derCert->buffer,
|
|
signer_x509->derCert->length, resp->heap);
|
|
if (ParseCertRelative(certDecoded, CERT_TYPE, NO_VERIFY,
|
|
NULL, NULL) == 0) {
|
|
if (OcspRespIdMatches(resp, certDecoded->subjectHash,
|
|
certDecoded->subjectKeyHash)) {
|
|
*signer = certDecoded;
|
|
*embedded = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
FreeDecodedCert(certDecoded);
|
|
}
|
|
|
|
if (flags & WOLFSSL_OCSP_NOINTERN) {
|
|
XFREE(certDecoded, resp->heap, DYNAMIC_TYPE_DCERT);
|
|
return ASN_NO_SIGNER_E;
|
|
}
|
|
|
|
/* not found in certs, search the cert embedded in the response */
|
|
InitDecodedCert(certDecoded, resp->cert, resp->certSz, resp->heap);
|
|
if (ParseCertRelative(certDecoded, CERT_TYPE, NO_VERIFY, NULL, NULL) == 0) {
|
|
if (OcspRespIdMatches(resp, certDecoded->subjectHash,
|
|
certDecoded->subjectKeyHash)) {
|
|
*signer = certDecoded;
|
|
*embedded = 1;
|
|
return 0;
|
|
}
|
|
}
|
|
FreeDecodedCert(certDecoded);
|
|
|
|
XFREE(certDecoded, resp->heap, DYNAMIC_TYPE_DCERT);
|
|
return ASN_NO_SIGNER_E;
|
|
}
|
|
|
|
static int OcspVerifySigner(WOLFSSL_OCSP_BASICRESP *resp, DecodedCert *cert,
|
|
WOLFSSL_X509_STORE *st, unsigned long flags)
|
|
{
|
|
WC_DECLARE_VAR(c, DecodedCert, 1, 0);
|
|
|
|
int ret = -1;
|
|
if (st == NULL)
|
|
return ASN_OCSP_CONFIRM_E;
|
|
|
|
WC_ALLOC_VAR_EX(c, DecodedCert, 1, NULL, DYNAMIC_TYPE_DCERT,
|
|
return MEMORY_E);
|
|
|
|
InitDecodedCert(c, cert->source, cert->maxIdx, NULL);
|
|
if (ParseCertRelative(c, CERT_TYPE, VERIFY, st->cm, NULL) != 0) {
|
|
ret = ASN_OCSP_CONFIRM_E;
|
|
goto err;
|
|
}
|
|
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
|
|
if ((flags & WOLFSSL_OCSP_NOCHECKS) == 0) {
|
|
ret = CheckOcspResponder(resp, c, st->cm);
|
|
}
|
|
else {
|
|
ret = 0;
|
|
}
|
|
#else
|
|
(void)resp;
|
|
(void)flags;
|
|
ret = 0;
|
|
#endif
|
|
|
|
err:
|
|
FreeDecodedCert(c);
|
|
WC_FREE_VAR_EX(c, NULL, DYNAMIC_TYPE_DCERT);
|
|
return ret;
|
|
}
|
|
/* Signature verified in DecodeBasicOcspResponse.
|
|
* But no store available to verify certificate. */
|
|
int wolfSSL_OCSP_basic_verify(WOLFSSL_OCSP_BASICRESP* bs,
|
|
WOLF_STACK_OF(WOLFSSL_X509) * certs, WOLFSSL_X509_STORE* st,
|
|
unsigned long flags)
|
|
{
|
|
int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
|
|
int embedded;
|
|
DecodedCert *cert = NULL;
|
|
|
|
ret = OcspFindSigner(bs, certs, &cert, &embedded, flags);
|
|
if (ret != 0) {
|
|
WOLFSSL_MSG("OCSP no signer found");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
/* skip certificate verification if cert in certs and TRUST_OTHER is true */
|
|
if (!embedded && (flags & WOLFSSL_OCSP_TRUSTOTHER) != 0)
|
|
flags |= WOLFSSL_OCSP_NOVERIFY;
|
|
|
|
/* verify response signature */
|
|
ret = ConfirmSignature(
|
|
&cert->sigCtx,
|
|
bs->response, bs->responseSz,
|
|
cert->publicKey, cert->pubKeySize, cert->keyOID,
|
|
bs->sig, bs->sigSz, bs->sigOID, bs->sigParams, bs->sigParamsSz,
|
|
NULL);
|
|
|
|
if (ret != 0) {
|
|
WOLFSSL_MSG("OCSP signature verification failed");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
if ((flags & WOLFSSL_OCSP_NOVERIFY) == 0) {
|
|
ret = OcspVerifySigner(bs, cert, st, flags);
|
|
}
|
|
|
|
err:
|
|
FreeDecodedCert(cert);
|
|
XFREE(cert, NULL, DYNAMIC_TYPE_DCERT);
|
|
return ret == 0 ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
|
|
}
|
|
|
|
void wolfSSL_OCSP_RESPONSE_free(OcspResponse* response)
|
|
{
|
|
OcspEntry *s, *sNext;
|
|
if (response == NULL)
|
|
return;
|
|
|
|
|
|
s = response->single;
|
|
while (s != NULL) {
|
|
sNext = s->next;
|
|
FreeOcspEntry(s, NULL);
|
|
XFREE(s, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
|
|
s = sNext;
|
|
}
|
|
|
|
XFREE(response->source, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
XFREE(response, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
|
|
}
|
|
|
|
#ifndef NO_BIO
|
|
OcspResponse* wolfSSL_d2i_OCSP_RESPONSE_bio(WOLFSSL_BIO* bio,
|
|
OcspResponse** response)
|
|
{
|
|
byte* data;
|
|
byte* p;
|
|
int len;
|
|
int dataAlloced = 0;
|
|
OcspResponse* ret = NULL;
|
|
|
|
if (bio == NULL)
|
|
return NULL;
|
|
|
|
if (bio->type == WOLFSSL_BIO_MEMORY) {
|
|
len = wolfSSL_BIO_get_mem_data(bio, &data);
|
|
if (len <= 0 || data == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
#ifndef NO_FILESYSTEM
|
|
else if (bio->type == WOLFSSL_BIO_FILE) {
|
|
long fcur;
|
|
long flen;
|
|
|
|
if (bio->ptr.fh == NULL)
|
|
return NULL;
|
|
|
|
fcur = XFTELL(bio->ptr.fh);
|
|
if (fcur < 0)
|
|
return NULL;
|
|
if(XFSEEK(bio->ptr.fh, 0, SEEK_END) != 0)
|
|
return NULL;
|
|
flen = XFTELL(bio->ptr.fh);
|
|
if (flen < 0)
|
|
return NULL;
|
|
if (XFSEEK(bio->ptr.fh, fcur, SEEK_SET) != 0)
|
|
return NULL;
|
|
|
|
/* check calculated length */
|
|
fcur = flen - fcur;
|
|
if (fcur > MAX_WOLFSSL_FILE_SIZE || fcur <= 0)
|
|
return NULL;
|
|
|
|
data = (byte*)XMALLOC((size_t)fcur, 0, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (data == NULL)
|
|
return NULL;
|
|
dataAlloced = 1;
|
|
|
|
len = wolfSSL_BIO_read(bio, (char *)data, (int)flen);
|
|
}
|
|
#endif
|
|
else
|
|
return NULL;
|
|
|
|
if (len > 0) {
|
|
p = data;
|
|
ret = wolfSSL_d2i_OCSP_RESPONSE(response, (const unsigned char **)&p,
|
|
len);
|
|
}
|
|
|
|
if (dataAlloced)
|
|
XFREE(data, 0, DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
return ret;
|
|
}
|
|
#endif /* !NO_BIO */
|
|
|
|
OcspResponse* wolfSSL_d2i_OCSP_RESPONSE(OcspResponse** response,
|
|
const unsigned char** data, int len)
|
|
{
|
|
OcspResponse *resp = NULL;
|
|
word32 idx = 0;
|
|
int length = 0;
|
|
int ret;
|
|
|
|
if (data == NULL)
|
|
return NULL;
|
|
|
|
if (response != NULL)
|
|
resp = *response;
|
|
if (resp == NULL) {
|
|
resp = (OcspResponse*)XMALLOC(sizeof(OcspResponse), NULL,
|
|
DYNAMIC_TYPE_OCSP_REQUEST);
|
|
if (resp == NULL)
|
|
return NULL;
|
|
XMEMSET(resp, 0, sizeof(OcspResponse));
|
|
}
|
|
|
|
resp->source = (byte*)XMALLOC((size_t)len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (resp->source == NULL) {
|
|
XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
|
|
return NULL;
|
|
}
|
|
resp->single = (OcspEntry*)XMALLOC(sizeof(OcspEntry), NULL,
|
|
DYNAMIC_TYPE_OCSP_ENTRY);
|
|
if (resp->single == NULL) {
|
|
XFREE(resp->source, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
|
|
return NULL;
|
|
}
|
|
XMEMSET(resp->single, 0, sizeof(OcspEntry));
|
|
resp->single->status = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL,
|
|
DYNAMIC_TYPE_OCSP_STATUS);
|
|
resp->single->ownStatus = 1;
|
|
if (resp->single->status == NULL) {
|
|
XFREE(resp->source, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(resp->single, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
|
|
XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
|
|
return NULL;
|
|
}
|
|
XMEMSET(resp->single->status, 0, sizeof(CertStatus));
|
|
|
|
XMEMCPY(resp->source, *data, (size_t)len);
|
|
resp->maxIdx = (word32)len;
|
|
|
|
ret = OcspResponseDecode(resp, NULL, NULL, 1, 1);
|
|
if (ret != 0 && ret != WC_NO_ERR_TRACE(ASN_OCSP_CONFIRM_E)) {
|
|
/* for just converting from a DER to an internal structure the CA may
|
|
* not yet be known to this function for signature verification */
|
|
wolfSSL_OCSP_RESPONSE_free(resp);
|
|
return NULL;
|
|
}
|
|
|
|
if (GetSequence(*data, &idx, &length, (word32)len) >= 0)
|
|
(*data) += (unsigned char) ((int)idx + length);
|
|
|
|
if (response != NULL && *response == NULL)
|
|
*response = resp;
|
|
|
|
return resp;
|
|
}
|
|
|
|
int wolfSSL_i2d_OCSP_RESPONSE(OcspResponse* response,
|
|
unsigned char** data)
|
|
{
|
|
if (data == NULL)
|
|
return (int)response->maxIdx;
|
|
|
|
XMEMCPY(*data, response->source, response->maxIdx);
|
|
return (int)response->maxIdx;
|
|
}
|
|
|
|
int wolfSSL_OCSP_response_status(OcspResponse *response)
|
|
{
|
|
return response->responseStatus;
|
|
}
|
|
|
|
const char *wolfSSL_OCSP_response_status_str(long s)
|
|
{
|
|
switch (s) {
|
|
case OCSP_SUCCESSFUL:
|
|
return "successful";
|
|
case OCSP_MALFORMED_REQUEST:
|
|
return "malformedrequest";
|
|
case OCSP_INTERNAL_ERROR:
|
|
return "internalerror";
|
|
case OCSP_TRY_LATER:
|
|
return "trylater";
|
|
case OCSP_SIG_REQUIRED:
|
|
return "sigrequired";
|
|
case OCSP_UNAUTHORIZED:
|
|
return "unauthorized";
|
|
default:
|
|
return "(UNKNOWN)";
|
|
}
|
|
}
|
|
|
|
WOLFSSL_OCSP_BASICRESP* wolfSSL_OCSP_response_get1_basic(OcspResponse* response)
|
|
{
|
|
WOLFSSL_OCSP_BASICRESP* bs;
|
|
const unsigned char *ptr = response->source;
|
|
|
|
bs = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, response->maxIdx);
|
|
return bs;
|
|
}
|
|
|
|
OcspRequest* wolfSSL_OCSP_REQUEST_new(void)
|
|
{
|
|
OcspRequest* request;
|
|
|
|
request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL,
|
|
DYNAMIC_TYPE_OPENSSL);
|
|
if (request != NULL)
|
|
XMEMSET(request, 0, sizeof(OcspRequest));
|
|
|
|
return request;
|
|
}
|
|
|
|
void wolfSSL_OCSP_REQUEST_free(OcspRequest* request)
|
|
{
|
|
FreeOcspRequest(request);
|
|
XFREE(request, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
}
|
|
|
|
int wolfSSL_i2d_OCSP_REQUEST(OcspRequest* request, unsigned char** data)
|
|
{
|
|
int size;
|
|
|
|
if (request == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
size = EncodeOcspRequest(request, NULL, 0);
|
|
if (size <= 0 || data == NULL)
|
|
return size;
|
|
|
|
return EncodeOcspRequest(request, *data, (word32) size);
|
|
}
|
|
|
|
WOLFSSL_OCSP_ONEREQ* wolfSSL_OCSP_request_add0_id(OcspRequest *req,
|
|
WOLFSSL_OCSP_CERTID *cid)
|
|
{
|
|
if (req == NULL || cid == NULL || cid->status == NULL)
|
|
return NULL;
|
|
|
|
if (req->cid != NULL)
|
|
wolfSSL_OCSP_CERTID_free((WOLFSSL_OCSP_CERTID*)req->cid);
|
|
/* Keep to free */
|
|
req->cid = (void*)cid;
|
|
|
|
XMEMCPY(req->issuerHash, cid->issuerHash, KEYID_SIZE);
|
|
XMEMCPY(req->issuerKeyHash, cid->issuerKeyHash, KEYID_SIZE);
|
|
if (cid->status->serialSz > req->serialSz) {
|
|
XFREE(req->serial, req->heap, DYNAMIC_TYPE_OCSP);
|
|
req->serial = (byte*)XMALLOC((size_t)cid->status->serialSz,
|
|
req->heap, DYNAMIC_TYPE_OCSP_REQUEST);
|
|
if (req->serial == NULL)
|
|
return NULL;
|
|
}
|
|
XMEMCPY(req->serial, cid->status->serial, (size_t)cid->status->serialSz);
|
|
req->serialSz = cid->status->serialSz;
|
|
|
|
return req;
|
|
}
|
|
|
|
WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_CERTID_dup(WOLFSSL_OCSP_CERTID* id)
|
|
{
|
|
WOLFSSL_OCSP_CERTID* certId;
|
|
|
|
if (id == NULL)
|
|
return NULL;
|
|
|
|
certId = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID),
|
|
NULL, DYNAMIC_TYPE_OPENSSL);
|
|
if (certId) {
|
|
XMEMCPY(certId, id, sizeof(WOLFSSL_OCSP_CERTID));
|
|
}
|
|
return certId;
|
|
}
|
|
|
|
#ifndef NO_BIO
|
|
int wolfSSL_i2d_OCSP_REQUEST_bio(WOLFSSL_BIO* out,
|
|
WOLFSSL_OCSP_REQUEST *req)
|
|
{
|
|
int size = -1;
|
|
unsigned char* data = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_i2d_OCSP_REQUEST_bio");
|
|
if (out == NULL || req == NULL)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
size = wolfSSL_i2d_OCSP_REQUEST(req, NULL);
|
|
if (size > 0) {
|
|
data = (unsigned char*) XMALLOC((size_t)size, out->heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
}
|
|
|
|
if (data != NULL) {
|
|
size = wolfSSL_i2d_OCSP_REQUEST(req, &data);
|
|
}
|
|
|
|
if (size <= 0) {
|
|
XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (wolfSSL_BIO_write(out, data, size) == (int)size) {
|
|
XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
#endif /* !NO_BIO */
|
|
|
|
int wolfSSL_i2d_OCSP_CERTID(WOLFSSL_OCSP_CERTID* id, unsigned char** data)
|
|
{
|
|
int allocated = 0;
|
|
word32 derSz = 0;
|
|
word32 intSz = 0;
|
|
int ret;
|
|
WOLFSSL_ENTER("wolfSSL_i2d_OCSP_CERTID");
|
|
|
|
if (id == NULL)
|
|
return -1;
|
|
|
|
if (id->rawCertId != NULL) {
|
|
derSz = id->rawCertIdSize;
|
|
}
|
|
else {
|
|
ret = OcspEncodeCertID(id, NULL, &derSz, &intSz);
|
|
if (ret != 0) {
|
|
WOLFSSL_MSG("Failed to calculate CertID size");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (data == NULL) {
|
|
return derSz;
|
|
}
|
|
|
|
if (*data == NULL) {
|
|
/* Allocate buffer for DER encoding */
|
|
*data = (byte*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
if (*data == NULL) {
|
|
WOLFSSL_MSG("Failed to allocate memory for CertID DER encoding");
|
|
return -1;
|
|
}
|
|
allocated = 1;
|
|
}
|
|
|
|
if (id->rawCertId != NULL) {
|
|
XMEMCPY(*data, id->rawCertId, id->rawCertIdSize);
|
|
}
|
|
else {
|
|
ret = OcspEncodeCertID(id, *data, &derSz, &intSz);
|
|
if (ret < 0) {
|
|
WOLFSSL_MSG("Failed to encode CertID");
|
|
if (allocated) {
|
|
XFREE(*data, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
*data = NULL;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (!allocated)
|
|
*data += derSz;
|
|
|
|
return derSz;
|
|
}
|
|
|
|
WOLFSSL_OCSP_CERTID* wolfSSL_d2i_OCSP_CERTID(WOLFSSL_OCSP_CERTID** cidOut,
|
|
const unsigned char** derIn,
|
|
int length)
|
|
{
|
|
WOLFSSL_OCSP_CERTID *cid = NULL;
|
|
int isAllocated = 0;
|
|
word32 idx = 0;
|
|
int ret;
|
|
|
|
if (derIn == NULL || *derIn == NULL || length <= 0)
|
|
return NULL;
|
|
|
|
if (cidOut != NULL && *cidOut != NULL) {
|
|
cid = *cidOut;
|
|
FreeOcspEntry(cid, NULL);
|
|
}
|
|
else {
|
|
cid = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID), NULL,
|
|
DYNAMIC_TYPE_OPENSSL);
|
|
if (cid == NULL)
|
|
return NULL;
|
|
isAllocated = 1;
|
|
}
|
|
|
|
XMEMSET(cid, 0, sizeof(WOLFSSL_OCSP_CERTID));
|
|
cid->status = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL,
|
|
DYNAMIC_TYPE_OCSP_STATUS);
|
|
if (cid->status == NULL) {
|
|
XFREE(cid, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
return NULL;
|
|
}
|
|
XMEMSET(cid->status, 0, sizeof(CertStatus));
|
|
cid->ownStatus = 1;
|
|
|
|
ret = OcspDecodeCertID(*derIn, &idx, length, cid);
|
|
if (ret != 0) {
|
|
FreeOcspEntry(cid, NULL);
|
|
if (isAllocated) {
|
|
XFREE(cid, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
*derIn += idx;
|
|
|
|
if (isAllocated && cidOut != NULL)
|
|
*cidOut = cid;
|
|
|
|
return cid;
|
|
}
|
|
|
|
const WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_SINGLERESP_get0_id(
|
|
const WOLFSSL_OCSP_SINGLERESP *single)
|
|
{
|
|
return single;
|
|
}
|
|
|
|
/**
|
|
* Compare two WOLFSSL_OCSP_CERTID objects
|
|
* @param a
|
|
* @param b
|
|
* @return 0 on success and when objects have the same id otherwise either
|
|
* the id's don't match or an error occurred
|
|
*/
|
|
int wolfSSL_OCSP_id_cmp(WOLFSSL_OCSP_CERTID *a, WOLFSSL_OCSP_CERTID *b)
|
|
{
|
|
int ret = 0;
|
|
if (a == NULL || b == NULL)
|
|
return WOLFSSL_FATAL_ERROR;
|
|
|
|
ret = a->hashAlgoOID != b->hashAlgoOID;
|
|
if (ret == 0)
|
|
ret = XMEMCMP(a->issuerHash, b->issuerHash, OCSP_DIGEST_SIZE);
|
|
if (ret == 0)
|
|
ret = XMEMCMP(a->issuerKeyHash, b->issuerKeyHash, OCSP_DIGEST_SIZE);
|
|
if (ret == 0) {
|
|
if (a->status != NULL && b->status != NULL) {
|
|
if (a->status->serialSz == b->status->serialSz)
|
|
ret = XMEMCMP(a->status->serial, b->status->serial,
|
|
(size_t)a->status->serialSz);
|
|
else
|
|
ret = -1;
|
|
}
|
|
else if (a->status != b->status) {
|
|
/* If either is not null then return non-zero */
|
|
ret = -1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int wolfSSL_OCSP_single_get0_status(WOLFSSL_OCSP_SINGLERESP *single,
|
|
int *reason,
|
|
WOLFSSL_ASN1_TIME **revtime,
|
|
WOLFSSL_ASN1_TIME **thisupd,
|
|
WOLFSSL_ASN1_TIME **nextupd)
|
|
{
|
|
if (single == NULL)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
#ifdef WOLFSSL_OCSP_PARSE_STATUS
|
|
if (thisupd != NULL)
|
|
*thisupd = &single->status->thisDateParsed;
|
|
if (nextupd != NULL)
|
|
*nextupd = &single->status->nextDateParsed;
|
|
#else
|
|
if (thisupd != NULL)
|
|
*thisupd = NULL;
|
|
if (nextupd != NULL)
|
|
*nextupd = NULL;
|
|
#endif
|
|
if (reason != NULL)
|
|
*reason = 0;
|
|
if (revtime != NULL)
|
|
*revtime = NULL;
|
|
|
|
return single->status->status;
|
|
}
|
|
|
|
int wolfSSL_OCSP_resp_count(WOLFSSL_OCSP_BASICRESP *bs)
|
|
{
|
|
WOLFSSL_OCSP_SINGLERESP* single;
|
|
int count = 0;
|
|
|
|
if (bs == NULL)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
single = bs->single;
|
|
while(single != NULL)
|
|
{
|
|
++count;
|
|
single = single->next;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
WOLFSSL_OCSP_SINGLERESP* wolfSSL_OCSP_resp_get0(WOLFSSL_OCSP_BASICRESP *bs, int idx)
|
|
{
|
|
WOLFSSL_OCSP_SINGLERESP* single;
|
|
int currIdx = 0;
|
|
|
|
if (bs == NULL)
|
|
return NULL;
|
|
|
|
single = bs->single;
|
|
while(single != NULL && currIdx != idx)
|
|
{
|
|
single = single->next;
|
|
++currIdx;
|
|
}
|
|
|
|
return single;
|
|
}
|
|
|
|
#endif /* OPENSSL_EXTRA */
|
|
|
|
#ifdef OPENSSL_ALL
|
|
|
|
/*******************************************************************************
|
|
* START OF WOLFSSL_OCSP_REQ_CTX API
|
|
******************************************************************************/
|
|
|
|
enum ocspReqStates {
|
|
ORS_INVALID = 0,
|
|
ORS_HEADER_ADDED,
|
|
ORS_REQ_DONE
|
|
};
|
|
|
|
enum ocspReqIOStates {
|
|
ORIOS_INVALID = 0,
|
|
ORIOS_WRITE,
|
|
ORIOS_READ
|
|
};
|
|
|
|
WOLFSSL_OCSP_REQ_CTX* wolfSSL_OCSP_REQ_CTX_new(WOLFSSL_BIO *bio, int maxline)
|
|
{
|
|
WOLFSSL_OCSP_REQ_CTX* ret = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_new");
|
|
|
|
if (maxline <= 0)
|
|
maxline = OCSP_MAX_REQUEST_SZ;
|
|
|
|
ret = (WOLFSSL_OCSP_REQ_CTX*)XMALLOC(sizeof(*ret), NULL,
|
|
DYNAMIC_TYPE_OPENSSL);
|
|
if (ret != NULL) {
|
|
XMEMSET(ret, 0, sizeof(*ret));
|
|
ret->buf = (byte*)XMALLOC((word32)maxline, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
if (ret->buf == NULL)
|
|
goto error;
|
|
ret->reqResp = wolfSSL_BIO_new(wolfSSL_BIO_s_mem());
|
|
ret->bufLen = maxline;
|
|
ret->bio = bio;
|
|
ret->ioState = ORIOS_WRITE;
|
|
}
|
|
|
|
return ret;
|
|
error:
|
|
wolfSSL_OCSP_REQ_CTX_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
void wolfSSL_OCSP_REQ_CTX_free(WOLFSSL_OCSP_REQ_CTX *ctx)
|
|
{
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_free");
|
|
if (ctx != NULL) {
|
|
if (ctx->buf != NULL)
|
|
XFREE(ctx->buf, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
if (ctx->reqResp != NULL)
|
|
wolfSSL_BIO_free(ctx->reqResp);
|
|
XFREE(ctx, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
}
|
|
}
|
|
|
|
WOLFSSL_OCSP_REQ_CTX* wolfSSL_OCSP_sendreq_new(WOLFSSL_BIO *bio,
|
|
const char *path, OcspRequest *req, int maxline)
|
|
{
|
|
WOLFSSL_OCSP_REQ_CTX* ret = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_sendreq_new");
|
|
|
|
ret = wolfSSL_OCSP_REQ_CTX_new(bio, maxline);
|
|
if (ret == NULL)
|
|
return NULL;
|
|
|
|
if (wolfSSL_OCSP_REQ_CTX_http(ret, "POST", path) != WOLFSSL_SUCCESS)
|
|
goto error;
|
|
|
|
if (req != NULL &&
|
|
wolfSSL_OCSP_REQ_CTX_set1_req(ret, req) != WOLFSSL_SUCCESS)
|
|
goto error;
|
|
|
|
return ret;
|
|
error:
|
|
wolfSSL_OCSP_REQ_CTX_free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
int wolfSSL_OCSP_REQ_CTX_add1_header(WOLFSSL_OCSP_REQ_CTX *ctx,
|
|
const char *name, const char *value)
|
|
{
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_add1_header");
|
|
|
|
if (name == NULL) {
|
|
WOLFSSL_MSG("Bad parameter");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
if (wolfSSL_BIO_puts(ctx->reqResp, name) <= 0) {
|
|
WOLFSSL_MSG("wolfSSL_BIO_puts error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
if (value != NULL) {
|
|
if (wolfSSL_BIO_write(ctx->reqResp, ": ", 2) != 2) {
|
|
WOLFSSL_MSG("wolfSSL_BIO_write error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
if (wolfSSL_BIO_puts(ctx->reqResp, value) <= 0) {
|
|
WOLFSSL_MSG("wolfSSL_BIO_puts error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
}
|
|
if (wolfSSL_BIO_write(ctx->reqResp, "\r\n", 2) != 2) {
|
|
WOLFSSL_MSG("wolfSSL_BIO_write error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
ctx->state = ORS_HEADER_ADDED;
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
int wolfSSL_OCSP_REQ_CTX_http(WOLFSSL_OCSP_REQ_CTX *ctx, const char *op,
|
|
const char *path)
|
|
{
|
|
static const char http_hdr[] = "%s %s HTTP/1.0\r\n";
|
|
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_http");
|
|
|
|
if (ctx == NULL || op == NULL) {
|
|
WOLFSSL_MSG("Bad parameter");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (path == NULL)
|
|
path = "/";
|
|
|
|
if (wolfSSL_BIO_printf(ctx->reqResp, http_hdr, op, path) <= 0) {
|
|
WOLFSSL_MSG("WOLFSSL_OCSP_REQ_CTX: wolfSSL_BIO_printf error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
ctx->state = ORS_HEADER_ADDED;
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
int wolfSSL_OCSP_REQ_CTX_set1_req(WOLFSSL_OCSP_REQ_CTX *ctx, OcspRequest *req)
|
|
{
|
|
static const char req_hdr[] =
|
|
"Content-Type: application/ocsp-request\r\n"
|
|
"Content-Length: %d\r\n\r\n";
|
|
/* Should be enough to hold Content-Length */
|
|
char req_hdr_buf[sizeof(req_hdr) + 10];
|
|
int req_hdr_buf_len;
|
|
int req_len = wolfSSL_i2d_OCSP_REQUEST(req, NULL);
|
|
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_set1_req");
|
|
|
|
if (ctx == NULL || req == NULL) {
|
|
WOLFSSL_MSG("Bad parameters");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (req_len <= 0) {
|
|
WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request len error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
req_hdr_buf_len =
|
|
XSNPRINTF(req_hdr_buf, sizeof(req_hdr_buf), req_hdr, req_len);
|
|
if (req_hdr_buf_len >= (int)sizeof(req_hdr_buf)) {
|
|
WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request too long");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (wolfSSL_BIO_write(ctx->reqResp, req_hdr_buf, req_hdr_buf_len) <= 0) {
|
|
WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: wolfSSL_BIO_write error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (wolfSSL_i2d_OCSP_REQUEST_bio(ctx->reqResp, req) <= 0) {
|
|
WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request i2d error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
ctx->state = ORS_REQ_DONE;
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
static int OCSP_REQ_CTX_bio_cb(char *buf, int sz, void *ctx)
|
|
{
|
|
return BioReceiveInternal((WOLFSSL_BIO*)ctx, NULL, buf, sz);
|
|
}
|
|
|
|
int wolfSSL_OCSP_REQ_CTX_nbio(WOLFSSL_OCSP_REQ_CTX *ctx)
|
|
{
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_nbio");
|
|
|
|
if (ctx == NULL) {
|
|
WOLFSSL_MSG("Bad parameters");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
switch ((enum ocspReqIOStates)ctx->ioState) {
|
|
case ORIOS_WRITE:
|
|
case ORIOS_READ:
|
|
break;
|
|
case ORIOS_INVALID:
|
|
default:
|
|
WOLFSSL_MSG("Invalid ctx->ioState state");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (ctx->ioState == ORIOS_WRITE) {
|
|
switch ((enum ocspReqStates)ctx->state) {
|
|
case ORS_HEADER_ADDED:
|
|
/* Write final new line to complete http header */
|
|
if (wolfSSL_BIO_write(ctx->reqResp, "\r\n", 2) != 2) {
|
|
WOLFSSL_MSG("wolfSSL_BIO_write error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
break;
|
|
case ORS_REQ_DONE:
|
|
break;
|
|
case ORS_INVALID:
|
|
default:
|
|
WOLFSSL_MSG("Invalid WOLFSSL_OCSP_REQ_CTX state");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
}
|
|
|
|
switch ((enum ocspReqIOStates)ctx->ioState) {
|
|
case ORIOS_WRITE:
|
|
{
|
|
const unsigned char *req;
|
|
int reqLen = wolfSSL_BIO_get_mem_data(ctx->reqResp, (void*)&req);
|
|
if (reqLen <= 0) {
|
|
WOLFSSL_MSG("wolfSSL_BIO_get_mem_data error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
while (ctx->sent < reqLen) {
|
|
int sent = wolfSSL_BIO_write(ctx->bio, req + ctx->sent,
|
|
reqLen - ctx->sent);
|
|
if (sent <= 0) {
|
|
if (wolfSSL_BIO_should_retry(ctx->bio))
|
|
return WOLFSSL_FATAL_ERROR;
|
|
WOLFSSL_MSG("wolfSSL_BIO_write error");
|
|
ctx->ioState = ORIOS_INVALID;
|
|
return 0;
|
|
}
|
|
ctx->sent += sent;
|
|
}
|
|
ctx->sent = 0;
|
|
ctx->ioState = ORIOS_READ;
|
|
(void)wolfSSL_BIO_reset(ctx->reqResp);
|
|
FALL_THROUGH;
|
|
}
|
|
case ORIOS_READ:
|
|
{
|
|
byte* resp = NULL;
|
|
int respLen;
|
|
int ret;
|
|
|
|
if (ctx->buf == NULL) /* Should be allocated in new call */
|
|
return WOLFSSL_FAILURE;
|
|
|
|
ret = wolfIO_HttpProcessResponseOcspGenericIO(OCSP_REQ_CTX_bio_cb,
|
|
ctx->bio, &resp, ctx->buf, ctx->bufLen, NULL);
|
|
if (ret <= 0) {
|
|
if (resp != NULL)
|
|
XFREE(resp, NULL, DYNAMIC_TYPE_OCSP);
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ) ||
|
|
ret == WC_NO_ERR_TRACE(OCSP_WANT_READ))
|
|
{
|
|
return WOLFSSL_FATAL_ERROR;
|
|
}
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
respLen = ret;
|
|
ret = wolfSSL_BIO_write(ctx->reqResp, resp, respLen);
|
|
XFREE(resp, NULL, DYNAMIC_TYPE_OCSP);
|
|
if (ret != respLen) {
|
|
WOLFSSL_MSG("wolfSSL_BIO_write error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
break;
|
|
}
|
|
case ORIOS_INVALID:
|
|
default:
|
|
WOLFSSL_MSG("Invalid ctx->ioState state");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
int wolfSSL_OCSP_sendreq_nbio(OcspResponse **presp, WOLFSSL_OCSP_REQ_CTX *ctx)
|
|
{
|
|
int ret;
|
|
int len;
|
|
const unsigned char *resp = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_sendreq_nbio");
|
|
|
|
if (presp == NULL)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
ret = wolfSSL_OCSP_REQ_CTX_nbio(ctx);
|
|
if (ret != WOLFSSL_SUCCESS)
|
|
return ret;
|
|
|
|
len = wolfSSL_BIO_get_mem_data(ctx->reqResp, (void*)&resp);
|
|
if (len <= 0)
|
|
return WOLFSSL_FAILURE;
|
|
return wolfSSL_d2i_OCSP_RESPONSE(presp, &resp, len) != NULL
|
|
? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* END OF WOLFSSL_OCSP_REQ_CTX API
|
|
******************************************************************************/
|
|
|
|
#ifndef NO_WOLFSSL_STUB
|
|
int wolfSSL_OCSP_REQUEST_add_ext(OcspRequest* req, WOLFSSL_X509_EXTENSION* ext,
|
|
int idx)
|
|
{
|
|
WOLFSSL_STUB("wolfSSL_OCSP_REQUEST_add_ext");
|
|
(void)req;
|
|
(void)ext;
|
|
(void)idx;
|
|
return WOLFSSL_FATAL_ERROR;
|
|
}
|
|
#endif
|
|
|
|
#ifndef NO_WOLFSSL_STUB
|
|
OcspResponse* wolfSSL_OCSP_response_create(int status,
|
|
WOLFSSL_OCSP_BASICRESP* bs)
|
|
{
|
|
WOLFSSL_STUB("wolfSSL_OCSP_response_create");
|
|
(void)status;
|
|
(void)bs;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#ifndef NO_WOLFSSL_STUB
|
|
const char* wolfSSL_OCSP_crl_reason_str(long s)
|
|
{
|
|
WOLFSSL_STUB("wolfSSL_OCSP_crl_reason_str");
|
|
(void)s;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/* Returns elements of an OCSP_CERTID struct. Currently only supports
|
|
* returning the serial number, and returns an error if user requests
|
|
* any of name, pmd, and/or keyHash.
|
|
* Return 1 on success, 0 on failure */
|
|
int wolfSSL_OCSP_id_get0_info(WOLFSSL_ASN1_STRING **name,
|
|
WOLFSSL_ASN1_OBJECT **pmd, WOLFSSL_ASN1_STRING **keyHash,
|
|
WOLFSSL_ASN1_INTEGER **serial, WOLFSSL_OCSP_CERTID *cid)
|
|
{
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_id_get0_info");
|
|
|
|
if (cid == NULL)
|
|
return 0;
|
|
|
|
/* build up ASN1_INTEGER for serial */
|
|
if (serial != NULL) {
|
|
int i = 0;
|
|
WOLFSSL_ASN1_INTEGER* ser;
|
|
|
|
ser = wolfSSL_ASN1_INTEGER_new();
|
|
if (ser == NULL)
|
|
return 0;
|
|
|
|
if (cid->status->serialSz > (WOLFSSL_ASN1_INTEGER_MAX - 2)) {
|
|
/* allocate data buffer, +2 for type and length */
|
|
ser->data = (unsigned char*)XMALLOC((size_t)cid->status->serialSz + 2, NULL,
|
|
DYNAMIC_TYPE_OPENSSL);
|
|
if (ser->data == NULL) {
|
|
wolfSSL_ASN1_INTEGER_free(ser);
|
|
return 0;
|
|
}
|
|
ser->dataMax = (unsigned int)cid->status->serialSz + 2;
|
|
ser->isDynamic = 1;
|
|
} else {
|
|
/* Use array instead of dynamic memory */
|
|
ser->data = ser->intData;
|
|
ser->dataMax = WOLFSSL_ASN1_INTEGER_MAX;
|
|
}
|
|
|
|
#if defined(WOLFSSL_QT) || defined(WOLFSSL_HAPROXY)
|
|
/* Serial number starts at 0 index of ser->data */
|
|
XMEMCPY(&ser->data[i], cid->status->serial,
|
|
(size_t)cid->status->serialSz);
|
|
ser->length = cid->status->serialSz;
|
|
#else
|
|
ser->data[i++] = ASN_INTEGER;
|
|
i += SetLength(cid->status->serialSz, ser->data + i);
|
|
XMEMCPY(&ser->data[i], cid->status->serial,
|
|
(size_t)cid->status->serialSz);
|
|
ser->length = i + cid->status->serialSz;
|
|
#endif
|
|
|
|
cid->status->serialInt = ser;
|
|
*serial = ser;
|
|
}
|
|
|
|
/* Not needed for Apache, return error if user is requesting */
|
|
if (name != NULL || pmd != NULL || keyHash != NULL) {
|
|
if (name != NULL)
|
|
*name = NULL;
|
|
|
|
if (pmd != NULL)
|
|
*pmd = NULL;
|
|
|
|
if (keyHash != NULL)
|
|
*keyHash = NULL;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int wolfSSL_OCSP_request_add1_nonce(OcspRequest* req, unsigned char* val,
|
|
int sz)
|
|
{
|
|
WC_RNG rng;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_request_add1_nonce");
|
|
|
|
if (req == NULL || sz > MAX_OCSP_NONCE_SZ) {
|
|
WOLFSSL_MSG("Bad parameter");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (sz <= 0)
|
|
sz = MAX_OCSP_NONCE_SZ;
|
|
|
|
if (val != NULL) {
|
|
XMEMCPY(req->nonce, val, (size_t)sz);
|
|
}
|
|
else {
|
|
if (
|
|
#ifndef HAVE_FIPS
|
|
wc_InitRng_ex(&rng, req->heap, INVALID_DEVID)
|
|
#else
|
|
wc_InitRng(&rng)
|
|
#endif
|
|
!= 0) {
|
|
WOLFSSL_MSG("RNG init failed");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
if (wc_RNG_GenerateBlock(&rng, req->nonce, (word32)sz) != 0) {
|
|
WOLFSSL_MSG("wc_RNG_GenerateBlock failed");
|
|
wc_FreeRng(&rng);
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
wc_FreeRng(&rng);
|
|
}
|
|
req->nonceSz = sz;
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
/* Returns result of OCSP nonce comparison. Return values:
|
|
* 1 - nonces are both present and equal
|
|
* 2 - both nonces are absent
|
|
* 3 - nonce only present in response
|
|
* -1 - nonce only present in request
|
|
* 0 - both nonces present and equal
|
|
*/
|
|
int wolfSSL_OCSP_check_nonce(OcspRequest* req, WOLFSSL_OCSP_BASICRESP* bs)
|
|
{
|
|
byte* reqNonce = NULL;
|
|
byte* rspNonce = NULL;
|
|
int reqNonceSz = 0;
|
|
int rspNonceSz = 0;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_OCSP_check_nonce");
|
|
|
|
if (req != NULL) {
|
|
reqNonce = req->nonce;
|
|
reqNonceSz = req->nonceSz;
|
|
}
|
|
|
|
if (bs != NULL) {
|
|
rspNonce = bs->nonce;
|
|
rspNonceSz = bs->nonceSz;
|
|
}
|
|
|
|
/* nonce absent in both req and rsp */
|
|
if (reqNonce == NULL && rspNonce == NULL)
|
|
return 2;
|
|
|
|
/* nonce present in rsp only */
|
|
if (reqNonce == NULL && rspNonce != NULL)
|
|
return 3;
|
|
|
|
/* nonce present in req only */
|
|
if (reqNonce != NULL && rspNonce == NULL)
|
|
return WOLFSSL_FATAL_ERROR;
|
|
|
|
/* nonces are present and equal, return 1. Extra NULL check for fixing
|
|
scan-build warning. */
|
|
if (reqNonceSz == rspNonceSz && reqNonce && rspNonce) {
|
|
if (XMEMCMP(reqNonce, rspNonce, (size_t)reqNonceSz) == 0)
|
|
return 1;
|
|
}
|
|
|
|
/* nonces are present but not equal */
|
|
return 0;
|
|
}
|
|
|
|
#endif /* OPENSSL_ALL */
|
|
|
|
#else /* HAVE_OCSP */
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
/* 4206 warning for blank file */
|
|
#pragma warning(disable: 4206)
|
|
#endif
|
|
|
|
|
|
#endif /* HAVE_OCSP */
|
|
#endif /* WOLFCRYPT_ONLY */
|