Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions certs/crl/bad_time_fmt.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-----BEGIN X509 CRL-----
MIIB7DCB1QIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzELMAkGA1UE
CAwCVVMxCzAJBgNVBAcMAlVTMQswCQYDVQQKDAJVUzELMAkGA1UEAwwCVVMxCzAJ
BgNVBAsMAlVTGA0yNDAxMjMwMDAwMDBaGA0zNDAxMjAwMDAwMDBaMDUwMwIUHIAC
LvgfJAXulqYS3LYf4KxwHl4XDTI1MDMxMzAyNDQ0MFowDDAKBgNVHRUEAwoBBqAc
MBowGAYDVR0UBBECDxnP/97adO3y9qRGDM7hQDANBgkqhkiG9w0BAQsFAAOCAQEA
aDY9jBdAJiAujUkaLYLVtzNWF/0SxD5CB4dYIcZMqtPKLn5ykcxkXvnRbVihJ+Kn
AAv9Fkn5iwj77EGwxNjyZktQ4gAmcMhCTBEcAHbmi92tHttot9Sr44+CN+0NaaQD
OflIeVw7Zir90TWufjScy8/e7FkVm+aD5CicrbJWqoe21pB1Q1jS49iNrZzqZ2vw
HLiqNAzpecxwUih/YPe5+CBk5Nq4vICeieGVC/JO9r5SkdDwWQTl0I3kSK6n4Jh7
53FmIen80F2ZZuZu4/fhJ7C4rlr6W9i6FrK06s5mk1PeYFHKhCkwI8wp8cIudJQD
lLsK2u4CTcuTKdbDLsszYA==
-----END X509 CRL-----
3 changes: 2 additions & 1 deletion certs/crl/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ EXTRA_DIST += \
certs/crl/wolfssl.cnf \
certs/crl/crl.der \
certs/crl/crl2.der \
certs/crl/crl_rsapss.pem
certs/crl/crl_rsapss.pem \
certs/crl/bad_time_fmt.pem

EXTRA_DIST += \
certs/crl/crl.revoked \
Expand Down
18 changes: 6 additions & 12 deletions src/x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -9046,13 +9046,10 @@ static int X509CRLPrintDates(WOLFSSL_BIO* bio, WOLFSSL_X509_CRL* crl,
}

if (crl->crlList->lastDate[0] != 0) {
if (GetTimeString(crl->crlList->lastDate, ASN_UTC_TIME,
if (GetTimeString(crl->crlList->lastDate, crl->crlList->lastDateFormat,
tmp, MAX_WIDTH) != WOLFSSL_SUCCESS) {
if (GetTimeString(crl->crlList->lastDate, ASN_GENERALIZED_TIME,
tmp, MAX_WIDTH) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("Error getting last update date");
return WOLFSSL_FAILURE;
}
WOLFSSL_MSG("Error getting last update date");
return WOLFSSL_FAILURE;
}
}
else {
Expand All @@ -9077,13 +9074,10 @@ static int X509CRLPrintDates(WOLFSSL_BIO* bio, WOLFSSL_X509_CRL* crl,
}

if (crl->crlList->nextDate[0] != 0) {
if (GetTimeString(crl->crlList->nextDate, ASN_UTC_TIME,
if (GetTimeString(crl->crlList->nextDate, crl->crlList->nextDateFormat,
tmp, MAX_WIDTH) != WOLFSSL_SUCCESS) {
if (GetTimeString(crl->crlList->nextDate, ASN_GENERALIZED_TIME,
tmp, MAX_WIDTH) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("Error getting next update date");
return WOLFSSL_FAILURE;
}
WOLFSSL_MSG("Error getting next update date");
return WOLFSSL_FAILURE;
}
}
else {
Expand Down
17 changes: 16 additions & 1 deletion tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -46392,10 +46392,25 @@ static int test_sk_X509_CRL(void)
ExpectIntEQ(BIO_get_mem_data(bio, NULL), 1324);
#endif
BIO_free(bio);
bio = NULL;
wolfSSL_X509_CRL_free(crl);
crl = NULL;

#ifndef NO_ASN_TIME
/* Test CRL with invalid GeneralizedTime */
ExpectNotNull(bio = BIO_new_file("./certs/crl/bad_time_fmt.pem", "rb"));
ExpectNotNull(crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL));
BIO_free(bio);
bio = NULL;
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
ExpectIntEQ(wolfSSL_X509_CRL_print(bio, crl), WOLFSSL_FAILURE);

BIO_free(bio);
bio = NULL;
wolfSSL_X509_CRL_free(crl);
crl = NULL;
#endif
#endif /* !NO_ASN_TIME */
#endif /* !NO_BIO */

#if !defined(NO_FILESYSTEM) && !defined(NO_STDIO_FILESYSTEM)
ExpectTrue((fp = XFOPEN("./certs/crl/crl.der", "rb")) != XBADFILE);
Expand Down
22 changes: 22 additions & 0 deletions wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -15984,6 +15984,28 @@ static WC_INLINE int GetTime_Long(long* value, const byte* date, int* idx)
int ExtractDate(const unsigned char* date, unsigned char format,
struct tm* certTime, int* idx)
{
int i = *idx;

/* Validate date string length based on format. Can not assume null
* terminated strings. Must check for the 'Z'.
* Subtract 2; one for zero indexing and one to exclude null terminator
* built into macro values. */
if (format == ASN_UTC_TIME) {
/* UTCTime format requires YYMMDDHHMMSSZ. */
if (date[i + ASN_UTC_TIME_SIZE - 2] != 'Z') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any guard to prevent an out-of-bounds read here, which would result in undefined behavior.

GetTimeString() is passed a len arg, but does not pass it on to ExtractDate(), and does not do any bounds checking of its own before passing date to ExtractDate().

Note that the unsafe logic can be reached from public API wolfSSL_ASN1_TIME_to_string(), also with no relevant bounds protection, so this is really not OK on that basis alone.

Bottom line, a refactor is needed to pass the len to ExtractDate() and use it for bounds checking.

Also note that the existing calls in ExtractDate() to btoi() and GetTime() are unsafe if date is not null-terminated. It is never safe to handle a non-null-terminated string without having and honoring an explicit length, but worse, there is no way to use btoi() and GetTime() safely on non-null-terminated strings.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@douzzer ,

Not sure how we can resolve this. Note the following:

int wc_ValidateDate(const byte* date, byte format, int dateType)
...
    if (!ExtractDate(date, format, &certTime, &i)) {
        WOLFSSL_MSG("Error extracting the date");
        return 0;
    }

No len is passed in to wc_ValidateDate(). It is a wolfcrypt local function, but it is macrofied to XVALIDATE_DATE . I think in order to get the level of safety you are looking for we need a major refactor which would fall way out of the scope of this PR.

I suggest your proposal be done in another PR. Let me know your thoughts.

return 0;
}
}
else if (format == ASN_GENERALIZED_TIME) {
/* GeneralizedTime format requires YYYYMMDDHHMMSSZ. */
if (date[ i + ASN_GENERALIZED_TIME_SIZE - 2] != 'Z') {
return 0;
}
}
else {
return 0;
}

XMEMSET(certTime, 0, sizeof(struct tm));

/* Get the first two bytes of the year (century) */
Expand Down