CRL improvements, add parsing for CRL number, do not allow CRL duplicates, add callback for when CRL entry is updated.

This commit is contained in:
Colton Willey
2024-09-23 11:52:39 -07:00
parent baab3348f4
commit 183aef241c
7 changed files with 373 additions and 39 deletions

151
src/crl.c
View File

@ -311,7 +311,6 @@ static int FindRevokedSerial(RevokedCert* rc, byte* serial, int serialSz,
#else
(void)totalCerts;
/* search in the linked list*/
while (rc) {
if (serialHash == NULL) {
if (rc->serialSz == serialSz &&
@ -560,12 +559,43 @@ int CheckCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert)
NULL, cert->extCrlInfo, cert->extCrlInfoSz, issuerName);
}
#ifdef HAVE_CRL_UPDATE_CB
static void SetCrlInfo(CRL_Entry* entry, CrlInfo *info) {
info->issuerHash = (byte *)entry->issuerHash;
info->issuerHashLen = CRL_DIGEST_SIZE;
info->lastDate = (byte *)entry->lastDate;
info->lastDateMaxLen = MAX_DATE_SIZE;
info->lastDateFormat = entry->lastDateFormat;
info->nextDate = (byte *)entry->nextDate;
info->nextDateMaxLen = MAX_DATE_SIZE;
info->nextDateFormat = entry->nextDateFormat;
info->crlNumber = (sword32)entry->crlNumber;
}
static void SetCrlInfoFromDecoded(DecodedCRL* entry, CrlInfo *info) {
info->issuerHash = (byte *)entry->issuerHash;
info->issuerHashLen = SIGNER_DIGEST_SIZE;
info->lastDate = (byte *)entry->lastDate;
info->lastDateMaxLen = MAX_DATE_SIZE;
info->lastDateFormat = entry->lastDateFormat;
info->nextDate = (byte *)entry->nextDate;
info->nextDateMaxLen = MAX_DATE_SIZE;
info->nextDateFormat = entry->nextDateFormat;
info->crlNumber = (sword32)entry->crlNumber;
}
#endif
/* Add Decoded CRL, 0 on success */
static int AddCRL(WOLFSSL_CRL* crl, DecodedCRL* dcrl, const byte* buff,
int verified)
{
CRL_Entry* crle = NULL;
CRL_Entry* curr = NULL;
CRL_Entry* prev = NULL;
#ifdef HAVE_CRL_UPDATE_CB
CrlInfo old;
CrlInfo new;
#endif
WOLFSSL_ENTER("AddCRL");
@ -594,8 +624,43 @@ static int AddCRL(WOLFSSL_CRL* crl, DecodedCRL* dcrl, const byte* buff,
return BAD_MUTEX_E;
}
crle->next = crl->crlList;
crl->crlList = crle;
for (curr = crl->crlList; curr != NULL; curr = curr->next) {
if (XMEMCMP(curr->issuerHash, crle->issuerHash, CRL_DIGEST_SIZE) == 0) {
if (crle->crlNumber <= curr->crlNumber) {
WOLFSSL_MSG("Same or newer CRL entry already exists");
CRL_Entry_free(crle, crl->heap);
wc_UnLockRwLock(&crl->crlLock);
return BAD_FUNC_ARG;
}
crle->next = curr->next;
if (prev != NULL) {
prev->next = crle;
}
else {
crl->crlList = crle;
}
#ifdef HAVE_CRL_UPDATE_CB
if (crl->cm && crl->cm->cbUpdateCRL != NULL) {
SetCrlInfo(curr, &old);
SetCrlInfo(crle, &new);
crl->cm->cbUpdateCRL(&old, &new);
}
#endif
break;
}
prev = curr;
}
if (curr != NULL) {
CRL_Entry_free(curr, crl->heap);
}
else {
crle->next = crl->crlList;
crl->crlList = crle;
}
wc_UnLockRwLock(&crl->crlLock);
/* Avoid heap-use-after-free after crl->crlList is released */
crl->currentEntry = NULL;
@ -686,6 +751,86 @@ int BufferLoadCRL(WOLFSSL_CRL* crl, const byte* buff, long sz, int type,
return ret ? ret : WOLFSSL_SUCCESS; /* convert 0 to WOLFSSL_SUCCESS */
}
#ifdef HAVE_CRL_UPDATE_CB
/* Fill out CRL info structure, WOLFSSL_SUCCESS on ok */
int GetCRLInfo(WOLFSSL_CRL* crl, CrlInfo* info, const byte* buff,
long sz, int type)
{
int ret = WOLFSSL_SUCCESS;
const byte* myBuffer = buff; /* if DER ok, otherwise switch */
DerBuffer* der = NULL;
CRL_Entry* crle = NULL;
#ifdef WOLFSSL_SMALL_STACK
DecodedCRL* dcrl;
#else
DecodedCRL dcrl[1];
#endif
WOLFSSL_ENTER("GetCRLInfo");
if (crl == NULL || info == NULL || buff == NULL || sz == 0)
return BAD_FUNC_ARG;
if (type == WOLFSSL_FILETYPE_PEM) {
#ifdef WOLFSSL_PEM_TO_DER
ret = PemToDer(buff, sz, CRL_TYPE, &der, NULL, NULL, NULL);
if (ret == 0) {
myBuffer = der->buffer;
sz = der->length;
}
else {
WOLFSSL_MSG("Pem to Der failed");
FreeDer(&der);
return -1;
}
#else
ret = NOT_COMPILED_IN;
#endif
}
#ifdef WOLFSSL_SMALL_STACK
dcrl = (DecodedCRL*)XMALLOC(sizeof(DecodedCRL), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (dcrl == NULL) {
FreeDer(&der);
return MEMORY_E;
}
#endif
crle = CRL_Entry_new(crl->heap);
if (crle == NULL) {
WOLFSSL_MSG("alloc CRL Entry failed");
#ifdef WOLFSSL_SMALL_STACK
XFREE(dcrl, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
FreeDer(&der);
return MEMORY_E;
}
InitDecodedCRL(dcrl, crl->heap);
ret = ParseCRL(crle->certs, dcrl, myBuffer, (word32)sz,
0, crl->cm);
if (ret != 0 && !(ret == WC_NO_ERR_TRACE(ASN_CRL_NO_SIGNER_E))) {
WOLFSSL_MSG("ParseCRL error");
CRL_Entry_free(crle, crl->heap);
crle = NULL;
}
else {
SetCrlInfoFromDecoded((DecodedCRL*)dcrl, info);
}
FreeDecodedCRL(dcrl);
#ifdef WOLFSSL_SMALL_STACK
XFREE(dcrl, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
FreeDer(&der);
CRL_Entry_free(crle, crl->heap);
return ret ? ret : WOLFSSL_SUCCESS; /* convert 0 to WOLFSSL_SUCCESS */
}
#endif
#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL)
/* helper function to create a new dynamic WOLFSSL_X509_CRL structure */
static WOLFSSL_X509_CRL* wolfSSL_X509_crl_new(WOLFSSL_CERT_MANAGER* cm)

View File

@ -1878,6 +1878,41 @@ int wolfSSL_CertManagerSetCRL_ErrorCb(WOLFSSL_CERT_MANAGER* cm, crlErrorCb cb,
return ret;
}
#ifdef HAVE_CRL_UPDATE_CB
int wolfSSL_CertManagerGetCRLInfo(WOLFSSL_CERT_MANAGER* cm, CrlInfo* info,
const byte* buff, long sz, int type)
{
return GetCRLInfo(cm->crl, info, buff, sz, type);
}
/* Set the callback to be called when a CRL entry has
* been updated (new entry had the same issuer hash and
* a newer CRL number).
*
* @param [in] cm Certificate manager.
* @param [in] cb CRL update callback.
* @return WOLFSSL_SUCCESS on success.
* @return BAD_FUNC_ARG when cm is NULL.
*/
int wolfSSL_CertManagerSetCRLUpdate_Cb(WOLFSSL_CERT_MANAGER* cm, CbUpdateCRL cb)
{
int ret = WOLFSSL_SUCCESS;
WOLFSSL_ENTER("wolfSSL_CertManagerSetCRLUpdate_Cb");
/* Validate parameters. */
if (cm == NULL) {
ret = BAD_FUNC_ARG;
}
if (ret == WOLFSSL_SUCCESS) {
/* Store callback. */
cm->cbUpdateCRL = cb;
}
return ret;
}
#endif
#ifdef HAVE_CRL_IO
/* Set the CRL I/O callback.
*

View File

@ -85769,46 +85769,130 @@ static int test_wolfSSL_CTX_LoadCRL(void)
return EXPECT_RESULT();
}
#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_CRL) && \
!defined(WOLFSSL_CRL_ALLOW_MISSING_CDP)
static int test_multiple_crls_same_issuer_ctx_ready(WOLFSSL_CTX* ctx)
#if defined(HAVE_CRL) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \
defined(HAVE_CRL_UPDATE_CB)
int crlUpdateTestStatus = 0;
WOLFSSL_CERT_MANAGER* updateCrlTestCm = NULL;
static void updateCrlCb(CrlInfo* old, CrlInfo* new)
{
EXPECT_DECLS;
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
ExpectIntEQ(wolfSSL_CTX_LoadCRLFile(ctx, "./certs/crl/crl.pem",
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
return EXPECT_RESULT();
const char* crl1 = "./certs/crl/crl.pem";
const char* crlRevoked = "./certs/crl/crl.revoked";
byte *crl1Buff = NULL;
word32 crl1Sz;
byte *crlRevBuff = NULL;
word32 crlRevSz;
WOLFSSL_CERT_MANAGER* cm = updateCrlTestCm;
XFILE f;
word32 sz;
CrlInfo crl1Info;
CrlInfo crlRevInfo;
crlUpdateTestStatus = 0;
if (old == NULL || new == NULL) {
return;
}
AssertTrue((f = XFOPEN(crl1, "rb")) != XBADFILE);
AssertTrue(XFSEEK(f, 0, XSEEK_END) == 0);
AssertIntGE(sz = (size_t) XFTELL(f), 1);
AssertTrue(XFSEEK(f, 0, XSEEK_SET) == 0);
AssertTrue( \
(crl1Buff = (byte*)XMALLOC(sz, NULL, DYNAMIC_TYPE_FILE)) != NULL);
AssertTrue(XFREAD(crl1Buff, 1, sz, f) == sz);
XFCLOSE(f);
crl1Sz = sz;
AssertTrue((f = XFOPEN(crlRevoked, "rb")) != XBADFILE);
AssertTrue(XFSEEK(f, 0, XSEEK_END) == 0);
AssertIntGE(sz = (size_t) XFTELL(f), 1);
AssertTrue(XFSEEK(f, 0, XSEEK_SET) == 0);
AssertTrue( \
(crlRevBuff = (byte*)XMALLOC(sz, NULL, DYNAMIC_TYPE_FILE)) != NULL);
AssertTrue(XFREAD(crlRevBuff, 1, sz, f) == sz);
XFCLOSE(f);
crlRevSz = sz;
AssertIntEQ(wolfSSL_CertManagerGetCRLInfo(
cm, &crl1Info, crl1Buff, crl1Sz, WOLFSSL_FILETYPE_PEM),
WOLFSSL_SUCCESS);
AssertIntEQ(wolfSSL_CertManagerGetCRLInfo(
cm, &crlRevInfo, crlRevBuff, crlRevSz, WOLFSSL_FILETYPE_PEM),
WOLFSSL_SUCCESS);
/* Old entry being replaced should match crl1 */
AssertIntEQ(crl1Info.issuerHashLen, old->issuerHashLen);
AssertIntEQ(crl1Info.lastDateMaxLen, old->lastDateMaxLen);
AssertIntEQ(crl1Info.lastDateFormat, old->lastDateFormat);
AssertIntEQ(crl1Info.nextDateMaxLen, old->nextDateMaxLen);
AssertIntEQ(crl1Info.nextDateFormat, old->nextDateFormat);
AssertIntEQ(crl1Info.crlNumber, old->crlNumber);
AssertIntEQ(XMEMCMP(
crl1Info.issuerHash, old->issuerHash, old->issuerHashLen), 0);
AssertIntEQ(XMEMCMP(
crl1Info.lastDate, old->lastDate, old->lastDateMaxLen), 0);
AssertIntEQ(XMEMCMP(
crl1Info.nextDate, old->nextDate, old->nextDateMaxLen), 0);
/* Newer entry should match crl revoked */
AssertIntEQ(crlRevInfo.issuerHashLen, new->issuerHashLen);
AssertIntEQ(crlRevInfo.lastDateMaxLen, new->lastDateMaxLen);
AssertIntEQ(crlRevInfo.lastDateFormat, new->lastDateFormat);
AssertIntEQ(crlRevInfo.nextDateMaxLen, new->nextDateMaxLen);
AssertIntEQ(crlRevInfo.nextDateFormat, new->nextDateFormat);
AssertIntEQ(crlRevInfo.crlNumber, new->crlNumber);
AssertIntEQ(XMEMCMP(
crlRevInfo.issuerHash, new->issuerHash, new->issuerHashLen), 0);
AssertIntEQ(XMEMCMP(
crlRevInfo.lastDate, new->lastDate, new->lastDateMaxLen), 0);
AssertIntEQ(XMEMCMP(
crlRevInfo.nextDate, new->nextDate, new->nextDateMaxLen), 0);
XFREE(crl1Buff, NULL, DYNAMIC_TYPE_FILE);
XFREE(crlRevBuff, NULL, DYNAMIC_TYPE_FILE);
crlUpdateTestStatus = 1;
}
#endif
static int test_multiple_crls_same_issuer(void)
static int test_wolfSSL_crl_update_cb(void)
{
EXPECT_DECLS;
#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_CRL) && \
!defined(WOLFSSL_CRL_ALLOW_MISSING_CDP)
test_ssl_cbf client_cbs, server_cbs;
struct {
const char* server_cert;
const char* server_key;
} test_params[] = {
{ "./certs/server-cert.pem", "./certs/server-key.pem" },
{ "./certs/server-revoked-cert.pem", "./certs/server-revoked-key.pem" }
};
size_t i;
#if defined(HAVE_CRL) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \
defined(HAVE_CRL_UPDATE_CB)
const char* crl1 = "./certs/crl/crl.pem";
const char* crlRevoked = "./certs/crl/crl.revoked";
const char* issuerCert = "./certs/client-cert.pem";
const char* caCert = "./certs/ca-cert.pem";
const char* goodCert = "./certs/server-cert.pem";
const char* revokedCert = "./certs/server-revoked-cert.pem";
int pemType = WOLFSSL_FILETYPE_PEM;
WOLFSSL_CERT_MANAGER* cm = NULL;
for (i = 0; i < (sizeof(test_params)/sizeof(*test_params)); i++) {
XMEMSET(&client_cbs, 0, sizeof(client_cbs));
XMEMSET(&server_cbs, 0, sizeof(server_cbs));
server_cbs.certPemFile = test_params[i].server_cert;
server_cbs.keyPemFile = test_params[i].server_key;
client_cbs.crlPemFile = "./certs/crl/extra-crls/general-server-crl.pem";
client_cbs.ctx_ready = test_multiple_crls_same_issuer_ctx_ready;
ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbs,
&server_cbs, NULL), -1001);
}
updateCrlTestCm = wolfSSL_CertManagerNew();
ExpectNotNull(updateCrlTestCm);
cm = updateCrlTestCm;
ExpectIntEQ(wolfSSL_CertManagerSetCRLUpdate_Cb(cm, updateCrlCb),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, caCert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, issuerCert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerLoadCRLFile(cm, crl1, pemType),
WOLFSSL_SUCCESS);
/* CRL1 does not have good cert revoked */
ExpectIntEQ(wolfSSL_CertManagerVerify(cm, goodCert, pemType),
WOLFSSL_SUCCESS);
ExpectIntNE(wolfSSL_CertManagerVerify(cm, revokedCert, pemType),
WOLFSSL_SUCCESS);
/* Load newer CRL from same issuer, callback verifies CRL entry details */
ExpectIntEQ(wolfSSL_CertManagerLoadCRLFile(cm, crlRevoked, pemType),
WOLFSSL_SUCCESS);
/* CRL callback verified entry info was as expected */
ExpectIntEQ(crlUpdateTestStatus, 1);
/* Ensure that both certs fail with newer CRL */
ExpectIntNE(wolfSSL_CertManagerVerify(cm, goodCert, pemType),
WOLFSSL_SUCCESS);
ExpectIntNE(wolfSSL_CertManagerVerify(cm, revokedCert, pemType),
WOLFSSL_SUCCESS);
#endif
return EXPECT_RESULT();
}
@ -96461,7 +96545,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_use_certificate_chain_file),
TEST_DECL(test_wolfSSL_CTX_trust_peer_cert),
TEST_DECL(test_wolfSSL_CTX_LoadCRL),
TEST_DECL(test_multiple_crls_same_issuer),
TEST_DECL(test_wolfSSL_crl_update_cb),
TEST_DECL(test_wolfSSL_CTX_SetTmpDH_file),
TEST_DECL(test_wolfSSL_CTX_SetTmpDH_buffer),
TEST_DECL(test_wolfSSL_CTX_SetMinMaxDhKey_Sz),

View File

@ -38280,6 +38280,41 @@ static int ParseCRL_AuthKeyIdExt(const byte* input, int sz, DecodedCRL* dcrl)
}
#endif
#ifdef WOLFSSL_ASN_TEMPLATE
static const ASNItem crlNumASN[] = {
/* CRL NUM */ {0, ASN_INTEGER, 0, 0, 0 },
};
enum {
CRLNUMASN_IDX_CRL_NUM = 0
};
/* Number of items in ASN.1 template for CrlNumber. */
#define crlNumASN_Length (sizeof(crlNumASN) / sizeof(ASNItem))
static int ParseCRL_CrlNumExt(const byte* input, int sz, DecodedCRL* dcrl)
{
DECL_ASNGETDATA(dataASN, crlNumASN_Length);
int ret = 0;
word32 idx = 0;
WOLFSSL_ENTER("ParseCRL_CrlNumExt");
CALLOC_ASNGETDATA(dataASN, crlNumASN_Length, ret, dcrl->heap);
if (ret == 0) {
GetASN_Int32Bit(&dataASN[CRLNUMASN_IDX_CRL_NUM], (word32 *)&dcrl->crlNumber);
/* Parse a CRL number. */
ret = GetASN_Items(crlNumASN, dataASN, crlNumASN_Length, 0, input,
&idx, (word32)sz);
}
FREE_ASNGETDATA(dataASN, dcrl->heap);
return ret;
#endif /* WOLFSSL_ASN_TEMPLATE */
}
#ifndef WOLFSSL_ASN_TEMPLATE
static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf,
@ -38472,7 +38507,14 @@ static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf, word32 idx,
}
#endif
}
/* TODO: Parse CRL Number extension */
else if (oid == CRL_NUMBER_OID) {
/* Parse CRL Number extension.
* idx is at start of OCTET_STRING data. */
ret = ParseCRL_CrlNumExt(buf + idx, length, dcrl);
if (ret != 0) {
WOLFSSL_MSG("\tcouldn't parse CRL Number extension");
}
}
/* TODO: check criticality */
/* Move index on to next extension. */
idx += (word32)length;

View File

@ -45,7 +45,10 @@ WOLFSSL_LOCAL int CheckCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert);
WOLFSSL_LOCAL int CheckCertCRL_ex(WOLFSSL_CRL* crl, byte* issuerHash,
byte* serial, int serialSz, byte* serialHash, const byte* extCrlInfo,
int extCrlInfoSz, void* issuerName);
#ifdef HAVE_CRL_UPDATE_CB
WOLFSSL_LOCAL int GetCRLInfo(WOLFSSL_CRL* crl, CrlInfo* info, const byte* buff,
long sz, int type);
#endif
#ifdef __cplusplus
} /* extern "C" */

View File

@ -2656,6 +2656,9 @@ struct WOLFSSL_CERT_MANAGER {
#ifdef WC_ASN_UNKNOWN_EXT_CB
wc_UnknownExtCallback unknownExtCallback;
#endif
#ifdef HAVE_CRL_UPDATE_CB
CbUpdateCRL cbUpdateCRL; /* notify thru cb that crl has updated */
#endif
};
WOLFSSL_LOCAL int CM_SaveCertCache(WOLFSSL_CERT_MANAGER* cm,

View File

@ -3324,6 +3324,22 @@ typedef void (*CbOCSPRespFree)(void*,unsigned char*);
typedef int (*CbCrlIO)(WOLFSSL_CRL* crl, const char* url, int urlSz);
#endif
#ifdef HAVE_CRL_UPDATE_CB
typedef struct CrlInfo {
byte *issuerHash;
word32 issuerHashLen;
byte *lastDate;
word32 lastDateMaxLen;
byte lastDateFormat;
byte *nextDate;
word32 nextDateMaxLen;
byte nextDateFormat;
sword32 crlNumber;
} CrlInfo;
typedef void (*CbUpdateCRL)(CrlInfo* old, CrlInfo* new);
#endif
/* User Atomic Record Layer CallBacks */
typedef int (*CallbackMacEncrypt)(WOLFSSL* ssl, unsigned char* macOut,
const unsigned char* macIn, unsigned int macInSz, int macContent,
@ -3771,6 +3787,12 @@ WOLFSSL_API void wolfSSL_CTX_SetPerformTlsRecordProcessingCb(WOLFSSL_CTX* ctx,
WOLFSSL_API int wolfSSL_CertManagerSetCRL_IOCb(WOLFSSL_CERT_MANAGER* cm,
CbCrlIO cb);
#endif
#ifdef HAVE_CRL_UPDATE_CB
WOLFSSL_API int wolfSSL_CertManagerGetCRLInfo(WOLFSSL_CERT_MANAGER* cm, CrlInfo* info,
const byte* buff, long sz, int type);
WOLFSSL_API int wolfSSL_CertManagerSetCRLUpdate_Cb(WOLFSSL_CERT_MANAGER* cm,
CbUpdateCRL cb);
#endif
#if defined(HAVE_OCSP)
WOLFSSL_API int wolfSSL_CertManagerCheckOCSPResponse(
WOLFSSL_CERT_MANAGER* cm, unsigned char *response, int responseSz,