Files
wolfssl/src/ocsp.c
T
Tobias Frauenschläger 1d15e1f27f Make OCSP stapling request ownership explicit
The OcspRequest carried a "void* ssl" back-pointer that the stapling
paths wrote just before handing the request to the OCSP layer. For the
request cached on the WOLFSSL_CTX that field is shared by every
connection using it, so concurrent handshakes raced on it. Drop the
field and pass the connection to CheckOcspRequest() and
CheckOcspResponse() as an argument instead, which is the only thing it
was ever read for.

Ownership of the cached request was equally implicit. Publication moves
out of CreateOcspRequest() into CreateOcspResponse(), and callers now
learn whether the CTX took ownership from a "ctxOwnsRequest" flag rather
than by comparing pointers against ssl->ctx->certOcspRequest, which was
read without the lock that guards it. The flag and the request are
handed back together on success and both left untouched on failure, so a
caller never decides ownership against a request it is not holding.

The cache is a field of the WOLFSSL_CTX, so serialize it with a lock
scoped to the CTX. SSL_CM(ssl) can resolve to a per-SSL cert manager
when WOLFSSL_LOCAL_X509_STORE is defined, which left two connections on
one CTX taking different locks for a check-then-set on the same pointer.
GetCtxOcspLock() keys off ssl->ctx->cm for both the reader and the
publisher, and a failure to take it is logged instead of silently
disabling the cache.

CheckOcspRequest() also loses its heap argument. It was only ever the
hint for the response buffer it hands back, which the caller frees
against the connection, so take it from the connection rather than from
a parameter every caller had to keep in step with its own free.

Smaller fixes in the same paths: zero the caller's response buffer
before the argument check can return, since SendCertificateStatus()
frees it without checking the return code; fold the ocsp_stapling NULL
check into the single early skip so the later uses need no guard;
gate the SetupOcspResp() free on success like the other two callers;
split the three differently owned requests in the
WOLFSSL_CSR2_OCSP_MULTI case into separate variables; and let that
case's allocation failures fall through to its shared cleanup instead of
returning, which leaked an already built leaf response.

Add test_ocsp_ctx_request_cache, which runs three handshakes over one
CTX pair and checks that the later ones reuse the cached request rather
than building another. The responder callback answers with a canned good
response, so stapling runs all the way through and the ownership
decision each connection makes is actually acted on: a connection that
freed the shared request shows up as a use after free on the next pass
and a double free at CTX teardown. The cached request is marked before
the last pass and the encoded request the callback sees is compared,
since a request rebuilt from the same certificate would otherwise be
identical byte for byte. The test is gated on !WOLFSSL_COPY_CERT:
OPENSSL_ALL implies it, and it gives every WOLFSSL its own certificate
copy, which takes the cache out of play. A new ocsp.yml job covers the
plain stapling build, an --enable-all build with the copy turned back
off, and an ASan build.

Also gate test_tls13_pha_status_request on KEEP_PEER_CERT. It checks the
received client certificate with wolfSSL_get_peer_certificate(), which is
only built when that macro is defined, so a post-handshake auth build with
stapling but without the OpenSSL compatibility layer failed to link
tests/unit.test.

Fixes F-7230 and F-7231.
2026-07-31 22:02:42 +02:00

2927 lines
84 KiB
C

/* ocsp.c
*
* Copyright (C) 2006-2026 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>
/*
* OCSP responder missing features:
* - Support for multiple requests and responses in a single OCSP exchange
* - Support name-based responder ID
* - Support for singleExtensions
*/
#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("wc_CheckCertOcspResponse", 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, NULL);
FreeOcspRequest(ocspRequest);
}
#ifdef WOLFSSL_SMALL_STACK
XFREE(ocspRequest, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
WOLFSSL_LEAVE("wc_CheckCertOcspResponse", 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;
void* heap = (ocsp->cm != NULL) ? ocsp->cm->heap : NULL;
WOLFSSL_ENTER("FreeOCSP");
for (entry = ocsp->ocspList; entry; entry = next) {
next = entry->next;
FreeOcspEntry(entry, heap);
XFREE(entry, heap, DYNAMIC_TYPE_OCSP_ENTRY);
}
wc_FreeMutex(&ocsp->ocspLock);
if (dynamic)
XFREE(ocsp, 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) {
ret = CheckOcspRequest(ocsp, ocspRequest, NULL, ssl);
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("GetOcspEntry", 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("GetOcspStatus", 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, MAX_DATE_SIZE)
&& ((*status)->nextDate[0] != 0)
&& XVALIDATE_DATE((*status)->nextDate,
(*status)->nextDateFormat, ASN_AFTER, MAX_DATE_SIZE))
#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
* ssl Connection the request belongs to, may be NULL.
* 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,
WOLFSSL* ssl)
{
#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("CheckOcspResponse", MEMORY_ERROR);
return MEMORY_E;
}
#endif
InitOcspResponse(ocspResponse, newSingle, newStatus, response,
(word32)responseSz, ocsp->cm->heap);
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(NO_TLS)
if (ssl != NULL && TLSX_CSR2_IsMulti(ssl->extensions)) {
ocspResponse->pendingCAs = TLSX_CSR2_GetPendingSigners(ssl->extensions);
}
#else
(void)ssl;
#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, WOLFSSL* ssl)
{
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;
void* ioCtx;
/* Hint for responseBuffer only, which the caller frees against the same
* connection, so take it from there rather than have every caller pass a
* heap it has to keep in step with its own free. */
void* heap = (ssl != NULL) ? ssl->heap : NULL;
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 IOCtx */
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 advertises no OCSP responder and no override URL is set, so
* OCSP has no opinion on this cert. Report that distinctly from a
* failed lookup; the caller owns the policy decision. Callers wanting
* the historical soft-fail run this through OcspNoUrlPolicy(). */
WOLFSSL_MSG("Cert has no OCSP URL");
return OCSP_NO_URL;
}
request = (byte*)XMALLOC((size_t)requestSz, ocsp->cm->heap, DYNAMIC_TYPE_OCSP);
if (request == NULL) {
WOLFSSL_LEAVE("CheckOcspRequest", 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, ssl);
}
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;
}
/* Map OCSP_NO_URL - "this cert advertises no OCSP responder" - to a
* verification result.
*
* WOLFSSL_OCSP_CHECKALL selects which certificates get checked, not how hard
* to fail when one cannot be checked, so it deliberately has no say here.
* Refuse the cert only when the user explicitly asked for that with
* WOLFSSL_OCSP_FAIL_IF_NOT_SUPPORTED.
*
* Returns OCSP_NEED_URL to refuse the cert, or 0 for the historical
* soft-fail. */
int OcspNoUrlPolicy(WOLFSSL_CERT_MANAGER* cm)
{
if (cm != NULL && cm->ocspFailIfNotSupported) {
WOLFSSL_MSG("Cert has no OCSP URL and OCSP is required for every cert");
WOLFSSL_ERROR_VERBOSE(OCSP_NEED_URL);
return OCSP_NEED_URL;
}
WOLFSSL_MSG("Cert has no OCSP URL, assuming CERT_GOOD");
return 0;
}
/* Enforce https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2. Both halves
* of CertID (issuerNameHash and issuerKeyHash) must match; name-only matching
* would authorize a same-DN / different-key CA. issuerKeyHash may be NULL when
* unavailable, which disables the delegated branch. */
int CheckOcspResponder(OcspResponse *bs, byte* subjectNameHash,
byte* subjectKeyHash, byte extExtKeyUsage, byte* issuerNameHash,
byte* issuerKeyHash)
{
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);
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 || subjectNameHash == NULL || issuerNameHash == NULL)
return BAD_FUNC_ARG;
for (single = bs->single; single != NULL; single = single->next) {
int passed = 0;
if (subjectKeyHash != NULL &&
XMEMCMP(subjectNameHash, single->issuerHash,
OCSP_DIGEST_SIZE) == 0 &&
XMEMCMP(subjectKeyHash, single->issuerKeyHash,
KEYID_SIZE) == 0) {
WOLFSSL_MSG("\tOCSP Response signed by issuer");
passed = 1;
}
else if ((extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) != 0) {
if (issuerKeyHash != NULL &&
XMEMCMP(issuerNameHash, single->issuerHash,
OCSP_DIGEST_SIZE) == 0 &&
XMEMCMP(issuerKeyHash, single->issuerKeyHash,
KEYID_SIZE) == 0) {
WOLFSSL_MSG("\tOCSP Response signed by authorized responder "
"delegated by issuer");
passed = 1;
}
}
if (!passed) {
WOLFSSL_MSG("\tOCSP Responder not authorized");
ret = BAD_OCSP_RESPONDER;
break;
}
}
return ret;
}
OcspRequest* wc_OcspRequest_new(void* heap)
{
OcspRequest* request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), heap,
DYNAMIC_TYPE_OCSP_REQUEST);
if (request != NULL) {
XMEMSET(request, 0, sizeof(OcspRequest));
request->heap = heap;
}
return request;
}
void wc_OcspRequest_free(OcspRequest* request)
{
if (request != NULL) {
FreeOcspRequest(request);
XFREE(request, request->heap, DYNAMIC_TYPE_OCSP_REQUEST);
}
}
OcspResponse* wc_OcspResponse_new(void* heap)
{
OcspResponse* response = (OcspResponse*)XMALLOC(sizeof(OcspResponse),
heap,
DYNAMIC_TYPE_OCSP_RESPONSE);
if (response != NULL) {
XMEMSET(response, 0, sizeof(OcspResponse));
response->heap = heap;
}
return response;
}
void wc_OcspResponse_free(OcspResponse* response)
{
if (response != NULL) {
FreeOcspResponse(response);
XFREE(response, response->heap, DYNAMIC_TYPE_OCSP_RESPONSE);
}
}
/* 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 (single->status != NULL && id->status != NULL &&
(single->status->serialSz == id->status->serialSz)
&& (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,
OCSP_RESPONDER_ID_KEY_SZ) == 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->subjectHash, c->subjectKeyHash,
c->extExtKeyUsage, c->issuerHash,
(c->ca != NULL) ? c->ca->subjectKeyHash : NULL);
}
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;
if (bs == NULL)
return WOLFSSL_FAILURE;
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 || *data == NULL || len <= 0)
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));
}
if (resp->source != NULL)
XFREE(resp->source, NULL, DYNAMIC_TYPE_TMP_BUFFER);
resp->source = (byte*)XMALLOC((size_t)len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (resp->source == NULL)
goto error;
if (resp->single != NULL) {
FreeOcspEntry(resp->single, NULL);
XFREE(resp->single, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
}
resp->single = (OcspEntry*)XMALLOC(sizeof(OcspEntry), NULL,
DYNAMIC_TYPE_OCSP_ENTRY);
if (resp->single == NULL)
goto error;
XMEMSET(resp->single, 0, sizeof(OcspEntry));
resp->single->status = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL,
DYNAMIC_TYPE_OCSP_STATUS);
if (resp->single->status == NULL)
goto error;
resp->single->ownStatus = 1;
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 */
goto error;
}
if (GetSequence(*data, &idx, &length, (word32)len) >= 0)
(*data) += idx + length;
if (response != NULL && *response == NULL)
*response = resp;
return resp;
error:
wolfSSL_OCSP_RESPONSE_free(resp);
if (response != NULL && *response == resp)
*response = NULL;
return NULL;
}
int wolfSSL_i2d_OCSP_RESPONSE(OcspResponse* response,
unsigned char** data)
{
if (response == NULL)
return BAD_FUNC_ARG;
if (response->source == NULL)
return BAD_FUNC_ARG;
if (data == NULL)
return (int)response->maxIdx;
if (*data == NULL) {
*data = (unsigned char*)XMALLOC(response->maxIdx, NULL,
DYNAMIC_TYPE_OPENSSL);
if (*data == NULL)
return -1;
XMEMCPY(*data, response->source, response->maxIdx);
}
else {
XMEMCPY(*data, response->source, response->maxIdx);
*data += response->maxIdx;
}
return (int)response->maxIdx;
}
int wolfSSL_OCSP_response_status(OcspResponse *response)
{
if (response == NULL)
return -1;
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;
if (response == NULL || response->source == NULL)
return NULL;
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;
size = EncodeOcspRequest(request, *data, (word32) size);
if (size > 0)
*data += size;
return 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, WC_MAX_DIGEST_SIZE);
XMEMCPY(req->issuerKeyHash, cid->issuerKeyHash, WC_MAX_DIGEST_SIZE);
req->hashAlg = (int)cid->hashAlgoOID;
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 == NULL)
return NULL;
XMEMCPY(certId, id, sizeof(WOLFSSL_OCSP_CERTID));
certId->next = NULL;
certId->rawCertId = NULL;
certId->rawCertIdSize = 0;
/* Deep-copy the status to avoid double-free */
if (id->status != NULL) {
certId->status = (CertStatus*)XMALLOC(sizeof(CertStatus),
NULL, DYNAMIC_TYPE_OCSP_STATUS);
if (certId->status == NULL) {
XFREE(certId, NULL, DYNAMIC_TYPE_OPENSSL);
return NULL;
}
XMEMCPY(certId->status, id->status, sizeof(CertStatus));
certId->status->next = NULL;
/* Don't share dynamically allocated fields */
certId->status->rawOcspResponse = NULL;
certId->status->rawOcspResponseSz = 0;
certId->status->serialInt = NULL;
#ifdef WOLFSSL_OCSP_PARSE_STATUS
certId->status->thisDateAsn = NULL;
certId->status->nextDateAsn = NULL;
#endif
}
certId->ownStatus = 1;
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) {
unsigned char* dataOrig = data;
size = wolfSSL_i2d_OCSP_REQUEST(req, &data);
data = dataOrig;
}
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) {
if (isAllocated) {
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 || single->status == NULL)
return -1;
#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;
}
/* Returns 0 if the string has no CR or LF, -1 if it does. */
static int OCSP_REQ_CTX_no_crlf(const char* value)
{
if (value != NULL) {
const char* c;
for (c = value; *c != '\0'; c++) {
if (*c == '\r' || *c == '\n')
return -1;
}
}
return 0;
}
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 (ctx == NULL || name == NULL) {
WOLFSSL_MSG("Bad parameter");
return WOLFSSL_FAILURE;
}
if (OCSP_REQ_CTX_no_crlf(name) != 0 || OCSP_REQ_CTX_no_crlf(value) != 0) {
WOLFSSL_MSG("CR/LF in header name or value");
return WOLFSSL_FAILURE;
}
/* A name starting with whitespace is an obs-fold continuation line
* (RFC 7230 Section 3.2.4) appending to the previous header's value. */
if (*name == ' ' || *name == '\t') {
WOLFSSL_MSG("Leading whitespace in header name");
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 (OCSP_REQ_CTX_no_crlf(op) != 0 || OCSP_REQ_CTX_no_crlf(path) != 0) {
WOLFSSL_MSG("CR/LF in HTTP op or path");
return WOLFSSL_FAILURE;
}
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)
{
const byte* reqNonce = NULL;
const 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 */
#ifdef HAVE_OCSP_RESPONDER
/* Free a CA entry and all its resources */
static void FreeOcspResponderCa(OcspResponderCa* ca, void* heap)
{
OcspResponderCertStatus* status;
OcspResponderCertStatus* nextStatus;
if (ca == NULL)
return;
/* Free private key */
#ifndef NO_RSA
if (ca->keyType == RSAk) {
wc_FreeRsaKey(&ca->key.rsa);
}
#endif
#ifdef HAVE_ECC
if (ca->keyType == ECDSAk) {
wc_ecc_free(&ca->key.ecc);
}
#endif
/* Free certificate DER if allocated */
if (ca->certDer != NULL) {
XFREE(ca->certDer, heap, DYNAMIC_TYPE_OCSP);
}
/* Free certificate status list */
status = ca->statuses;
while (status != NULL) {
nextStatus = status->next;
XFREE(status, heap, DYNAMIC_TYPE_OCSP);
status = nextStatus;
}
XFREE(ca, heap, DYNAMIC_TYPE_OCSP);
}
/* Allocate and initialize an OCSP Responder */
OcspResponder* wc_OcspResponder_new(void* heap, int sendCerts)
{
OcspResponder* responder;
WOLFSSL_ENTER("wc_OcspResponder_new");
responder = (OcspResponder*)XMALLOC(sizeof(OcspResponder), heap,
DYNAMIC_TYPE_OCSP);
if (responder != NULL) {
XMEMSET(responder, 0, sizeof(OcspResponder));
responder->heap = heap;
responder->sendCerts = sendCerts ? 1 : 0;
if (wc_InitRng(&responder->rng) != 0) {
XFREE(responder, heap, DYNAMIC_TYPE_OCSP);
responder = NULL;
}
}
return responder;
}
/* Free an OCSP Responder */
void wc_OcspResponder_free(OcspResponder* responder)
{
WOLFSSL_ENTER("wc_OcspResponder_free");
if (responder != NULL) {
OcspResponderCa* ca;
OcspResponderCa* nextCa;
/* Free all CAs */
ca = responder->caList;
while (ca != NULL) {
nextCa = ca->next;
FreeOcspResponderCa(ca, responder->heap);
ca = nextCa;
}
wc_FreeRng(&responder->rng);
XFREE(responder, responder->heap, DYNAMIC_TYPE_OCSP);
}
}
int wc_OcspResponder_AddSigner(OcspResponder* responder,
const byte* signerDer, word32 signerDerSz,
const byte* keyDer, word32 keyDerSz,
/* Necessary when adding an authorized responder */
const byte* issuerCertDer, word32 issuerCertDerSz)
{
int ret = -1;
OcspResponderCa* ca = NULL;
DecodedCert* decoded = NULL;
word32 keyOID = 0;
WOLFSSL_ENTER("wc_OcspResponder_AddSigner");
if (responder == NULL || signerDer == NULL || signerDerSz == 0 ||
keyDer == NULL || keyDerSz == 0 ||
(issuerCertDerSz != 0 && issuerCertDer == NULL))
return BAD_FUNC_ARG;
/* Allocate CA structure */
ca = (OcspResponderCa*)XMALLOC(sizeof(OcspResponderCa), responder->heap,
DYNAMIC_TYPE_OCSP);
if (ca == NULL) {
ret = MEMORY_E;
goto out;
}
XMEMSET(ca, 0, sizeof(OcspResponderCa));
/* Parse certificate */
decoded = (DecodedCert*)XMALLOC(sizeof(DecodedCert), responder->heap,
DYNAMIC_TYPE_OCSP);
if (decoded == NULL) {
ret = MEMORY_E;
goto out;
}
wc_InitDecodedCert(decoded, signerDer, signerDerSz, responder->heap);
ret = wc_ParseCert(decoded, CERT_TYPE, 0, NULL);
if (ret != 0)
goto out;
ret = wc_CheckPrivateKeyCert(keyDer, keyDerSz, decoded, 1, responder->heap);
if (ret != 1) {
ret = (ret == 0) ? BAD_FUNC_ARG : ret;
goto out;
}
keyOID = decoded->keyOID;
ret = wc_ShaHash(decoded->publicKeyForHash, decoded->pubKeyForHashSize,
ca->responderKeyHash);
if (ret != 0)
goto out;
/* Now place the real issuer into decoded and perform cert can actually
* sign for issuer. */
if (issuerCertDer != NULL && issuerCertDerSz > 0) {
char issuer[WC_ASN_NAME_MAX];
if (signerDerSz == issuerCertDerSz &&
XMEMCMP(signerDer, issuerCertDer, signerDerSz) == 0) {
/* API misuse */
ret = BAD_FUNC_ARG;
goto out;
}
if ((decoded->extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) == 0) {
/* Cert is not authorized for OCSP signing */
ret = BAD_FUNC_ARG;
goto out;
}
XMEMCPY(issuer, decoded->issuer, WC_ASN_NAME_MAX);
/* Use allocated DecodedCert */
wc_FreeDecodedCert(decoded);
wc_InitDecodedCert(decoded, issuerCertDer, issuerCertDerSz,
responder->heap);
ret = wc_ParseCert(decoded, CERT_TYPE, 0, NULL);
if (ret != 0)
goto out;
if (XSTRNCMP(issuer, decoded->subject, WC_ASN_NAME_MAX) != 0) {
/* Issuer name in responder cert does not match subject of issuer cert */
ret = BAD_FUNC_ARG;
goto out;
}
ca->authResp = 1;
}
/* Use responder cert's subject and public key for hashes */
if (decoded->subjectRawForHash == NULL ||
decoded->subjectRawForHashLen <= 0) {
ret = BAD_FUNC_ARG;
goto out;
}
ret = AsnHashesHash(&ca->issuerHashes, decoded->subjectRawForHash,
(word32)decoded->subjectRawForHashLen);
if (ret != 0)
goto out;
if (decoded->publicKeyForHash == NULL || decoded->pubKeyForHashSize == 0) {
ret = BAD_FUNC_ARG;
goto out;
}
ret = AsnHashesHash(&ca->issuerKeyHashes, decoded->publicKeyForHash,
decoded->pubKeyForHashSize);
if (ret != 0)
goto out;
/* Extract necessary info from decoded cert */
XMEMCPY(ca->subject, decoded->subject, WC_ASN_NAME_MAX);
/* Store raw certificate DER if sendCerts is enabled */
if (responder->sendCerts) {
ca->certDer = (byte*)XMALLOC(signerDerSz, responder->heap, DYNAMIC_TYPE_OCSP);
if (ca->certDer == NULL) {
ret = MEMORY_E;
goto out;
}
XMEMCPY(ca->certDer, signerDer, signerDerSz);
ca->certDerSz = signerDerSz;
}
/* Load the private key */
#ifndef NO_RSA
if (keyOID == RSAk) {
ret = wc_InitRsaKey(&ca->key.rsa, responder->heap);
if (ret == 0) {
word32 idx = 0;
ca->keyType = RSAk;
ret = wc_RsaPrivateKeyDecode(keyDer, &idx, &ca->key.rsa, keyDerSz);
}
if (ret != 0)
goto out;
}
else
#endif
#ifdef HAVE_ECC
if (keyOID == ECDSAk) {
ret = wc_ecc_init_ex(&ca->key.ecc, responder->heap, INVALID_DEVID);
if (ret == 0) {
word32 idx = 0;
ca->keyType = ECDSAk;
ret = wc_EccPrivateKeyDecode(keyDer, &idx, &ca->key.ecc, keyDerSz);
}
if (ret != 0)
goto out;
}
else
#endif
{
ret = NOT_COMPILED_IN;
goto out;
}
/* Check for duplicates before adding */
{
OcspResponderCa* existing = responder->caList;
while (existing != NULL) {
if (XMEMCMP(&existing->issuerHashes, &ca->issuerHashes,
sizeof(AsnHashes)) == 0 &&
XMEMCMP(&existing->issuerKeyHashes, &ca->issuerKeyHashes,
sizeof(AsnHashes)) == 0) {
ret = DUPE_ENTRY_E;
goto out;
}
existing = existing->next;
}
}
/* Add CA to list */
ca->next = responder->caList;
responder->caList = ca;
ca = NULL; /* Ownership transferred */
ret = 0;
out:
if (decoded != NULL) {
wc_FreeDecodedCert(decoded);
XFREE(decoded, responder->heap, DYNAMIC_TYPE_OCSP);
}
if (ca != NULL)
FreeOcspResponderCa(ca, responder->heap);
return ret;
}
/* Find Auth CA by issuer hashes from request */
static OcspResponderCa* FindCaByHashes(OcspResponder* responder,
const byte* issuerHash, const byte* issuerKeyHash, int hashAlg)
{
OcspResponderCa* ca = responder->caList;
while (ca != NULL) {
int hashSz = 0;
const byte* caIssuerHash = AsnHashesGetHash(&ca->issuerHashes,
hashAlg, &hashSz);
const byte* caKeyHash = AsnHashesGetHash(&ca->issuerKeyHashes,
hashAlg, &hashSz);
if (caIssuerHash != NULL && caKeyHash != NULL && hashSz > 0 &&
XMEMCMP(caIssuerHash, issuerHash, (size_t)hashSz) == 0 &&
XMEMCMP(caKeyHash, issuerKeyHash, (size_t)hashSz) == 0) {
return ca;
}
ca = ca->next;
}
return NULL;
}
/* Find certificate status in a CA */
static OcspResponderCertStatus* FindCertStatus(OcspResponderCa* ca,
const byte* serial, word32 serialSz)
{
OcspResponderCertStatus* status = ca->statuses;
while (status != NULL) {
if (status->serialSz == (int)serialSz &&
XMEMCMP(status->serial, serial, serialSz) == 0) {
return status;
}
status = status->next;
}
return NULL;
}
/* Find Auth CA by subject string */
static OcspResponderCa* FindCaBySubject(OcspResponder* responder,
const char* caSubject, word32 caSubjectSz)
{
OcspResponderCa* ca = responder->caList;
while (ca != NULL) {
word32 subjectLen = (word32)XSTRLEN(ca->subject);
if (subjectLen == caSubjectSz &&
XMEMCMP(ca->subject, caSubject, caSubjectSz) == 0) {
return ca;
}
ca = ca->next;
}
return NULL;
}
/* Add a certificate status for a specific CA */
int wc_OcspResponder_SetCertStatus(OcspResponder* responder,
const char* caSubject, word32 caSubjectSz,
const byte* serial, word32 serialSz, enum Ocsp_Cert_Status status,
time_t revocationTime, enum WC_CRL_Reason revocationReason,
word32 validityPeriod)
{
OcspResponderCa* ca = NULL;
OcspResponderCertStatus* certStatus = NULL;
int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
int isNew = 0;
WOLFSSL_ENTER("wc_OcspResponder_SetCertStatus");
if (responder == NULL || caSubject == NULL || caSubjectSz == 0 ||
serial == NULL || serialSz == 0 || serialSz > EXTERNAL_SERIAL_SIZE)
goto out;
if (status != CERT_GOOD && status != CERT_REVOKED &&
status != CERT_UNKNOWN)
goto out;
/* Validate revocation parameters when status is REVOKED */
if (status == CERT_REVOKED) {
if (revocationTime <= 0)
goto out;
if (revocationReason < WC_CRL_REASON_UNSPECIFIED ||
revocationReason > WC_CRL_REASON_AA_COMPROMISE)
goto out;
/* Skip value 7 which is not used */
if (revocationReason == 7)
goto out;
}
if (status == CERT_GOOD && validityPeriod == 0)
goto out;
if (status != CERT_GOOD && validityPeriod != 0)
goto out;
/* Find the CA */
ca = FindCaBySubject(responder, caSubject, caSubjectSz);
if (ca == NULL) {
ret = ASN_NO_SIGNER_E;
goto out;
}
/* Check if status already exists for this serial */
certStatus = FindCertStatus(ca, serial, serialSz);
if (certStatus == NULL) {
/* Allocate new status entry */
certStatus = (OcspResponderCertStatus*)XMALLOC(
sizeof(OcspResponderCertStatus), responder->heap, DYNAMIC_TYPE_OCSP);
if (certStatus == NULL) {
ret = MEMORY_E;
goto out;
}
XMEMSET(certStatus, 0, sizeof(OcspResponderCertStatus));
XMEMCPY(certStatus->serial, serial, serialSz);
certStatus->serialSz = serialSz;
isNew = 1;
}
certStatus->status = status;
certStatus->validityPeriod = 0;
if (status == CERT_REVOKED) {
ret = GetFormattedTime_ex(&revocationTime, certStatus->revocationDate,
sizeof(certStatus->revocationDate), ASN_GENERALIZED_TIME);
if (ret <= 0) {
WOLFSSL_MSG("Failed to format revocation time");
goto out;
}
certStatus->revocationDateSz = (word32)ret;
certStatus->revocationReason = revocationReason;
}
else if (status == CERT_GOOD) {
certStatus->validityPeriod = validityPeriod;
}
if (isNew) {
/* Add to CA's status list */
certStatus->next = ca->statuses;
ca->statuses = certStatus;
certStatus = NULL; /* Ownership transferred */
}
ret = 0;
out:
if (isNew && certStatus != NULL)
XFREE(certStatus, responder->heap, DYNAMIC_TYPE_OCSP);
return ret;
}
static int OcspResponse_WriteResponse(OcspResponder* responder, byte* response,
word32* responseSz, OcspResponderCa* ca,
OcspResponderCertStatus* certStatus, OcspRequest* req)
{
OcspResponse resp;
CertStatus status;
OcspEntry entry;
int ret = 0;
time_t now;
RsaKey* rsaKey = NULL;
ecc_key* eccKey = NULL;
int respInited = 0;
int hashSz = 0;
const byte* caIssuerHash = NULL;
const byte* caKeyHash = NULL;
int hashAlg = -1;
WOLFSSL_ASSERT_SIZEOF_EQ(resp.responderId.keyHash, ca->responderKeyHash);
WOLFSSL_ENTER("OcspResponse_WriteResponse");
if (responseSz == NULL || ca == NULL || certStatus == NULL ||
certStatus->serialSz > EXTERNAL_SERIAL_SIZE) {
ret = BAD_FUNC_ARG;
goto out;
}
InitOcspResponse(&resp, &entry, &status, NULL, 0, responder->heap);
respInited = 1;
resp.responseStatus = OCSP_SUCCESSFUL;
status.status = certStatus->status;
XMEMCPY(status.serial, certStatus->serial, certStatus->serialSz);
status.serialSz = (byte)certStatus->serialSz;
/* Copy revocation information if status is REVOKED */
if (certStatus->status == CERT_REVOKED) {
XMEMCPY(status.revocationDate, certStatus->revocationDate,
certStatus->revocationDateSz);
status.revocationDateSz = certStatus->revocationDateSz;
status.revocationReason = (byte)certStatus->revocationReason;
}
now = wc_Time(NULL);
ret = GetFormattedTime_ex(&now, status.thisDate, sizeof(status.thisDate),
ASN_GENERALIZED_TIME);
if (ret <= 0) {
WOLFSSL_MSG("Failed to format thisUpdate time");
goto out;
}
XMEMCPY(resp.producedDate, status.thisDate, ret);
resp.producedDateSz = status.thisDateSz = (byte)(ret);
resp.producedDateFormat = status.thisDateFormat = ASN_GENERALIZED_TIME;
/* Set nextUpdate if validity period is specified (for CERT_GOOD) */
if (certStatus->status == CERT_GOOD && certStatus->validityPeriod > 0) {
time_t nextTime = now + (time_t)certStatus->validityPeriod;
ret = GetFormattedTime_ex(&nextTime, status.nextDate,
sizeof(status.nextDate), ASN_GENERALIZED_TIME);
if (ret <= 0) {
WOLFSSL_MSG("Failed to format nextUpdate time");
goto out;
}
status.nextDateSz = (byte)ret;
status.nextDateFormat = ASN_GENERALIZED_TIME;
}
/* Set certificate if sendCerts is enabled */
if (responder->sendCerts && ca->certDer != NULL) {
resp.cert = ca->certDer;
resp.certSz = ca->certDerSz;
}
/* Copy nonce from request to response if present */
if (req != NULL && req->nonceSz > 0) {
resp.nonce = req->nonce;
resp.nonceSz = req->nonceSz;
}
/* Echo the hash algorithm from the request */
if (req == NULL) {
hashAlg = SHAh; /* Fall back to SHA-1 as its required for OCSP */
}
else {
hashAlg = req->hashAlg;
}
caIssuerHash = AsnHashesGetHash(&ca->issuerHashes, hashAlg, &hashSz);
caKeyHash = AsnHashesGetHash(&ca->issuerKeyHashes, hashAlg, &hashSz);
if (caIssuerHash == NULL || caKeyHash == NULL || hashSz <= 0) {
ret = ASN_SIG_HASH_E;
goto out;
}
entry.hashAlgoOID = (word32)hashAlg;
XMEMCPY(entry.issuerHash, caIssuerHash, (size_t)hashSz);
XMEMCPY(entry.issuerKeyHash, caKeyHash, (size_t)hashSz);
resp.responderIdType = OCSP_RESPONDER_ID_KEY;
XMEMCPY(resp.responderId.keyHash, ca->responderKeyHash, WC_SHA_DIGEST_SIZE);
/* TODO allow user to set algo */
if (ca->keyType == RSAk) {
rsaKey = &ca->key.rsa;
resp.sigOID = CTC_SHA256wRSA;
}
else if (ca->keyType == ECDSAk) {
eccKey = &ca->key.ecc;
resp.sigOID = CTC_SHA256wECDSA;
}
else {
ret = NOT_COMPILED_IN;
goto out;
}
ret = OcspResponseEncode(&resp, response, responseSz, rsaKey, eccKey,
&responder->rng);
if (ret != 0) {
WOLFSSL_MSG("Failed to encode OCSP response");
goto out;
}
ret = 0;
out:
if (respInited)
FreeOcspResponse(&resp);
return ret;
}
/* Generate OCSP response for a request */
int wc_OcspResponder_WriteResponse(OcspResponder* responder,
const byte* request, word32 requestSz,
byte* response, word32* responseSz)
{
int ret = 0;
OcspRequest req;
OcspResponderCa* ca = NULL;
OcspResponderCertStatus* certStatus = NULL;
int reqInited = 0;
WOLFSSL_ENTER("wc_OcspResponder_WriteResponse");
if (responder == NULL || request == NULL || requestSz == 0) {
ret = BAD_FUNC_ARG;
goto out;
}
XMEMSET(&req, 0, sizeof(OcspRequest));
/* Decode the OCSP request */
ret = DecodeOcspRequest(&req, request, requestSz);
if (ret != 0) {
WOLFSSL_MSG("Failed to decode OCSP request");
goto out;
}
reqInited = 1;
/* Find the CA by issuer hashes */
ca = FindCaByHashes(responder, req.issuerHash, req.issuerKeyHash,
req.hashAlg);
if (ca == NULL) {
WOLFSSL_MSG("No matching CA found for request");
ret = ASN_NO_SIGNER_E;
goto out;
}
/* Find the certificate status */
certStatus = FindCertStatus(ca, req.serial, req.serialSz);
if (certStatus == NULL) {
/* RFC 6960: 'unknown' is a per-certificate status inside a successful
* OCSPResponse, not an error response. Generate a successful response
* with CERT_UNKNOWN so clients can distinguish it from UNAUTHORIZED. */
OcspResponderCertStatus unknownStatus;
WOLFSSL_MSG("No status for requested certificate, responding unknown");
if (req.serialSz > EXTERNAL_SERIAL_SIZE) {
ret = BUFFER_E;
goto out;
}
XMEMSET(&unknownStatus, 0, sizeof(unknownStatus));
XMEMCPY(unknownStatus.serial, req.serial, req.serialSz);
unknownStatus.serialSz = req.serialSz;
unknownStatus.status = CERT_UNKNOWN;
ret = OcspResponse_WriteResponse(responder, response, responseSz, ca,
&unknownStatus, &req);
}
else {
WOLFSSL_MSG("Found CA and certificate status");
ret = OcspResponse_WriteResponse(responder, response, responseSz, ca,
certStatus, &req);
}
out:
if (reqInited)
FreeOcspRequest(&req);
WOLFSSL_LEAVE("wc_OcspResponder_WriteResponse", ret);
return ret;
}
int wc_OcspResponder_WriteErrorResponse(enum Ocsp_Response_Status status,
byte* response, word32* responseSz)
{
int ret = 0;
OcspResponse resp;
int respInited = 0;
WOLFSSL_ENTER("wc_OcspResponder_WriteErrorResponse");
if (responseSz == NULL) {
ret = BAD_FUNC_ARG;
goto out;
}
/* Validate status - OCSP_SUCCESSFUL is not allowed for error responses */
if (status == OCSP_SUCCESSFUL) {
WOLFSSL_MSG("OCSP_SUCCESSFUL is not a valid error status");
ret = BAD_FUNC_ARG;
goto out;
}
/* Validate that status is a known enumeration value */
if (status != OCSP_MALFORMED_REQUEST &&
status != OCSP_INTERNAL_ERROR &&
status != OCSP_TRY_LATER &&
status != OCSP_SIG_REQUIRED &&
status != OCSP_UNAUTHORIZED) {
WOLFSSL_MSG("Invalid OCSP response status");
ret = BAD_FUNC_ARG;
goto out;
}
/* Initialize a minimal OCSP response structure */
InitOcspResponse(&resp, NULL, NULL, NULL, 0, NULL);
respInited = 1;
/* Set the error status */
resp.responseStatus = (byte)status;
/* Encode the error response (no responseBytes, just status) */
ret = OcspResponseEncode(&resp, response, responseSz, NULL, NULL, NULL);
if (ret != 0) {
WOLFSSL_MSG("Failed to encode OCSP error response");
goto out;
}
ret = 0;
out:
if (respInited)
FreeOcspResponse(&resp);
return ret;
}
#endif /* HAVE_OCSP_RESPONDER */
/* Helper functions for testing */
int wc_InitOcspRequest(OcspRequest* req, DecodedCert* cert,
byte useNonce, void* heap)
{
return InitOcspRequest(req, cert, useNonce, heap);
}
int wc_EncodeOcspRequest(OcspRequest* req, byte* output,
word32 size)
{
return EncodeOcspRequest(req, output, size);
}
#else /* HAVE_OCSP */
#ifdef _MSC_VER
/* 4206 warning for blank file */
#pragma warning(disable: 4206)
#endif
#endif /* HAVE_OCSP */
#endif /* WOLFCRYPT_ONLY */