Skip to content

Commit

Permalink
Merge pull request #6887 from julek-wolfssl/zd/16849
Browse files Browse the repository at this point in the history
Implement untrusted certs in wolfSSL_X509_STORE_CTX_init
  • Loading branch information
JacobBarthelmeh authored Nov 6, 2023
2 parents 8569e76 + d13d446 commit c92d258
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 63 deletions.
32 changes: 32 additions & 0 deletions src/ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -24285,6 +24285,38 @@ WOLFSSL_STACK* wolfSSL_sk_dup(WOLFSSL_STACK* sk)
return NULL;
}


WOLFSSL_STACK* wolfSSL_shallow_sk_dup(WOLFSSL_STACK* sk)
{

WOLFSSL_STACK* ret = NULL;
WOLFSSL_STACK** prev = &ret;

WOLFSSL_ENTER("wolfSSL_shallow_sk_dup");

for (; sk != NULL; sk = sk->next) {
WOLFSSL_STACK* cur = wolfSSL_sk_new_node(sk->heap);

if (!cur) {
WOLFSSL_MSG("wolfSSL_sk_new_node error");
goto error;
}

XMEMCPY(cur, sk, sizeof(WOLFSSL_STACK));
cur->next = NULL;

*prev = cur;
prev = &cur->next;
}
return ret;

error:
if (ret) {
wolfSSL_sk_free(ret);
}
return NULL;
}

/* Free the just the stack structure */
void wolfSSL_sk_free(WOLFSSL_STACK* sk)
{
Expand Down
1 change: 1 addition & 0 deletions src/ssl_certman.c
Original file line number Diff line number Diff line change
Expand Up @@ -1648,6 +1648,7 @@ int wolfSSL_CertManagerDisableCRL(WOLFSSL_CERT_MANAGER* cm)
if (ret == WOLFSSL_SUCCESS) {
/* Disable CRL checking. */
cm->crlEnabled = 0;
cm->crlCheckAll = 0;
}

return ret;
Expand Down
142 changes: 80 additions & 62 deletions src/x509_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,40 @@ int wolfSSL_X509_STORE_CTX_init(WOLFSSL_X509_STORE_CTX* ctx,
#endif

ctx->chain = sk;
/* Add intermediate certificates from stack to store */
while (sk != NULL) {
WOLFSSL_X509* x509_cert = sk->data.x509;
if (x509_cert != NULL && x509_cert->isCa) {
ret = wolfSSL_X509_STORE_add_cert(store, x509_cert);
if (ret < 0) {
return WOLFSSL_FAILURE;
/* Add intermediate certs, that verify to a loaded CA, to the store */
if (sk != NULL) {
byte addedAtLeastOne = 1;
WOLF_STACK_OF(WOLFSSL_X509)* head = wolfSSL_shallow_sk_dup(sk);
if (head == NULL)
return WOLFSSL_FAILURE;
while (addedAtLeastOne) {
WOLF_STACK_OF(WOLFSSL_X509)* cur = head;
WOLF_STACK_OF(WOLFSSL_X509)** prev = &head;
addedAtLeastOne = 0;
while (cur) {
WOLFSSL_X509* cert = cur->data.x509;
if (cert != NULL && cert->derCert != NULL &&
wolfSSL_CertManagerVerifyBuffer(store->cm,
cert->derCert->buffer,
cert->derCert->length,
WOLFSSL_FILETYPE_ASN1) == WOLFSSL_SUCCESS) {
ret = wolfSSL_X509_STORE_add_cert(store, cert);
if (ret < 0) {
wolfSSL_sk_free(head);
return WOLFSSL_FAILURE;
}
addedAtLeastOne = 1;
*prev = cur->next;
wolfSSL_sk_free_node(cur);
cur = *prev;
}
else {
prev = &cur->next;
cur = cur->next;
}
}
}
sk = sk->next;
wolfSSL_sk_free(head);
}

ctx->sesChain = NULL;
Expand Down Expand Up @@ -140,7 +164,9 @@ void wolfSSL_X509_STORE_CTX_free(WOLFSSL_X509_STORE_CTX* ctx)
}
}


/* Its recommended to use a full free -> init cycle of all the objects
* because wolfSSL_X509_STORE_CTX_init may modify the store too which doesn't
* get reset here. */
void wolfSSL_X509_STORE_CTX_cleanup(WOLFSSL_X509_STORE_CTX* ctx)
{
if (ctx != NULL) {
Expand Down Expand Up @@ -168,7 +194,7 @@ int GetX509Error(int e)
{
switch (e) {
case ASN_BEFORE_DATE_E:
return WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD;
return WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID;
case ASN_AFTER_DATE_E:
return WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED;
case ASN_NO_SIGNER_E: /* get issuer error if no CA found locally */
Expand All @@ -185,6 +211,9 @@ int GetX509Error(int e)
return WOLFSSL_X509_V_ERR_CERT_SIGNATURE_FAILURE;
case CRL_CERT_REVOKED:
return WOLFSSL_X509_V_ERR_CERT_REVOKED;
case 0:
case 1:
return 0;
default:
#ifdef HAVE_WOLFSSL_MSG_EX
WOLFSSL_MSG_EX("Error not configured or implemented yet: %d", e);
Expand All @@ -195,6 +224,19 @@ int GetX509Error(int e)
}
}

static void SetupStoreCtxError(WOLFSSL_X509_STORE_CTX* ctx, int ret)
{
int depth = 0;
int error = GetX509Error(ret);

/* Set error depth */
if (ctx->chain)
depth = (int)ctx->chain->num;

wolfSSL_X509_STORE_CTX_set_error(ctx, error);
wolfSSL_X509_STORE_CTX_set_error_depth(ctx, depth);
}

/* Verifies certificate chain using WOLFSSL_X509_STORE_CTX
* returns 0 on success or < 0 on failure.
*/
Expand All @@ -204,66 +246,39 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)

if (ctx != NULL && ctx->store != NULL && ctx->store->cm != NULL
&& ctx->current_cert != NULL && ctx->current_cert->derCert != NULL) {
int ret = 0;
int depth = 0;
int error;
#ifndef NO_ASN_TIME
byte *afterDate, *beforeDate;
#endif

ret = wolfSSL_CertManagerVerifyBuffer(ctx->store->cm,
int ret = wolfSSL_CertManagerVerifyBuffer(ctx->store->cm,
ctx->current_cert->derCert->buffer,
ctx->current_cert->derCert->length,
WOLFSSL_FILETYPE_ASN1);
/* If there was an error, process it and add it to CTX */
if (ret < 0) {
/* Get corresponding X509 error */
error = GetX509Error(ret);
/* Set error depth */
if (ctx->chain)
depth = (int)ctx->chain->num;

wolfSSL_X509_STORE_CTX_set_error(ctx, error);
wolfSSL_X509_STORE_CTX_set_error_depth(ctx, depth);
#if defined(OPENSSL_ALL) || defined(WOLFSSL_QT)
if (ctx->store && ctx->store->verify_cb)
ctx->store->verify_cb(0, ctx);
#endif
}
SetupStoreCtxError(ctx, ret);

#ifndef NO_ASN_TIME
error = 0;
/* wolfSSL_CertManagerVerifyBuffer only returns ASN_AFTER_DATE_E or
ASN_BEFORE_DATE_E if there are no additional errors found in the
cert. Therefore, check if the cert is expired or not yet valid
in order to return the correct expected error. */
afterDate = ctx->current_cert->notAfter.data;
beforeDate = ctx->current_cert->notBefore.data;

if (XVALIDATE_DATE(afterDate, (byte)ctx->current_cert->notAfter.type,
AFTER) < 1) {
error = WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED;
}
else if (XVALIDATE_DATE(beforeDate,
(byte)ctx->current_cert->notBefore.type, BEFORE) < 1) {
error = WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID;
if (ret != ASN_BEFORE_DATE_E && ret != ASN_AFTER_DATE_E) {
/* wolfSSL_CertManagerVerifyBuffer only returns ASN_AFTER_DATE_E or
ASN_BEFORE_DATE_E if there are no additional errors found in the
cert. Therefore, check if the cert is expired or not yet valid
in order to return the correct expected error. */
byte *afterDate = ctx->current_cert->notAfter.data;
byte *beforeDate = ctx->current_cert->notBefore.data;

if (XVALIDATE_DATE(afterDate,
(byte)ctx->current_cert->notAfter.type, AFTER) < 1) {
ret = ASN_AFTER_DATE_E;
}
else if (XVALIDATE_DATE(beforeDate,
(byte)ctx->current_cert->notBefore.type, BEFORE) < 1) {
ret = ASN_BEFORE_DATE_E;
}
SetupStoreCtxError(ctx, ret);
}
#endif

if (error != 0 ) {
wolfSSL_X509_STORE_CTX_set_error(ctx, error);
wolfSSL_X509_STORE_CTX_set_error_depth(ctx, depth);
#if defined(OPENSSL_ALL) || defined(WOLFSSL_QT)
if (ctx->store && ctx->store->verify_cb)
ctx->store->verify_cb(0, ctx);
#endif
}
#if defined(OPENSSL_ALL) || defined(WOLFSSL_QT)
if (ctx->store && ctx->store->verify_cb)
ret = ctx->store->verify_cb(ret >= 0 ? 1 : 0, ctx) == 1 ? 0 : -1;
#endif

/* OpenSSL returns 0 when a chain can't be built */
if (ret == ASN_NO_SIGNER_E)
return WOLFSSL_FAILURE;
else
return ret;
return ret >= 0 ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
}
return WOLFSSL_FATAL_ERROR;
}
Expand Down Expand Up @@ -1029,8 +1044,11 @@ WOLFSSL_API int wolfSSL_X509_STORE_load_locations(WOLFSSL_X509_STORE *str,

#ifdef HAVE_CRL
if (str->cm->crl == NULL) {
/* Workaround to allocate the internals to load CRL's but don't enable
* CRL checking by default */
if (wolfSSL_CertManagerEnableCRL(str->cm, WOLFSSL_CRL_CHECK)
!= WOLFSSL_SUCCESS) {
!= WOLFSSL_SUCCESS ||
wolfSSL_CertManagerDisableCRL(str->cm) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("Enable CRL failed");
wolfSSL_CTX_free(ctx);
return WOLFSSL_FAILURE;
Expand Down
116 changes: 115 additions & 1 deletion tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -35531,6 +35531,118 @@ static int test_wolfSSL_X509_STORE_CTX(void)
return EXPECT_RESULT();
}

#if defined(OPENSSL_EXTRA) && !defined(NO_RSA)
static int test_X509_STORE_untrusted_load_cert_to_stack(const char* filename,
STACK_OF(X509)* chain)
{
EXPECT_DECLS;
XFILE fp = XBADFILE;
X509* cert = NULL;

ExpectTrue((fp = XFOPEN(filename, "rb"))
!= XBADFILE);
ExpectNotNull(cert = PEM_read_X509(fp, 0, 0, 0 ));
if (fp != XBADFILE) {
XFCLOSE(fp);
fp = XBADFILE;
}
ExpectIntEQ(sk_X509_push(chain, cert), 1);
if (EXPECT_FAIL())
X509_free(cert);

return EXPECT_RESULT();
}

static int test_X509_STORE_untrusted_certs(const char** filenames, int ret,
int err, int loadCA)
{
EXPECT_DECLS;
X509_STORE_CTX* ctx = NULL;
X509_STORE* str = NULL;
XFILE fp = XBADFILE;
X509* cert = NULL;
STACK_OF(X509)* untrusted = NULL;

ExpectTrue((fp = XFOPEN("./certs/intermediate/server-int-cert.pem", "rb"))
!= XBADFILE);
ExpectNotNull(cert = PEM_read_X509(fp, 0, 0, 0 ));
if (fp != XBADFILE) {
XFCLOSE(fp);
fp = XBADFILE;
}

ExpectNotNull(str = X509_STORE_new());
ExpectNotNull(ctx = X509_STORE_CTX_new());
ExpectNotNull(untrusted = sk_X509_new_null());

ExpectIntEQ(X509_STORE_set_flags(str, 0), 1);
if (loadCA) {
ExpectIntEQ(X509_STORE_load_locations(str, "./certs/ca-cert.pem", NULL),
1);
}
for (; *filenames; filenames++) {
ExpectIntEQ(test_X509_STORE_untrusted_load_cert_to_stack(*filenames,
untrusted), TEST_SUCCESS);
}

ExpectIntEQ(X509_STORE_CTX_init(ctx, str, cert, untrusted), 1);
ExpectIntEQ(X509_verify_cert(ctx), ret);
ExpectIntEQ(X509_STORE_CTX_get_error(ctx), err);

X509_free(cert);
X509_STORE_free(str);
X509_STORE_CTX_free(ctx);
sk_X509_pop_free(untrusted, NULL);

return EXPECT_RESULT();
}
#endif

static int test_X509_STORE_untrusted(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && !defined(NO_RSA)
const char* untrusted1[] = {
"./certs/intermediate/ca-int2-cert.pem",
NULL
};
const char* untrusted2[] = {
"./certs/intermediate/ca-int-cert.pem",
"./certs/intermediate/ca-int2-cert.pem",
NULL
};
const char* untrusted3[] = {
"./certs/intermediate/ca-int-cert.pem",
"./certs/intermediate/ca-int2-cert.pem",
"./certs/ca-cert.pem",
NULL
};
/* Adding unrelated certs that should be ignored */
const char* untrusted4[] = {
"./certs/client-ca.pem",
"./certs/intermediate/ca-int-cert.pem",
"./certs/server-cert.pem",
"./certs/intermediate/ca-int2-cert.pem",
NULL
};

/* Only immediate issuer in untrusted chaing. Fails since can't build chain
* to loaded CA. */
ExpectIntEQ(test_X509_STORE_untrusted_certs(untrusted1, 0,
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 1), TEST_SUCCESS);
/* Succeeds because path to loaded CA is available. */
ExpectIntEQ(test_X509_STORE_untrusted_certs(untrusted2, 1, 0, 1),
TEST_SUCCESS);
/* Fails because root CA is in the untrusted stack */
ExpectIntEQ(test_X509_STORE_untrusted_certs(untrusted3, 0,
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 0), TEST_SUCCESS);
/* Succeeds because path to loaded CA is available. */
ExpectIntEQ(test_X509_STORE_untrusted_certs(untrusted4, 1, 0, 1),
TEST_SUCCESS);
#endif
return EXPECT_RESULT();
}

static int test_wolfSSL_X509_STORE_set_flags(void)
{
EXPECT_DECLS;
Expand Down Expand Up @@ -51492,7 +51604,8 @@ static int test_X509_LOOKUP_add_dir(void)

/* Now we SHOULD get CRL_MISSING, because we looked for PEM
* in dir containing only ASN1/DER. */
ExpectIntEQ(X509_verify_cert(storeCtx), CRL_MISSING);
ExpectIntEQ(X509_verify_cert(storeCtx), WOLFSSL_FAILURE);
ExpectIntEQ(X509_STORE_CTX_get_error(storeCtx), CRL_MISSING);

X509_CRL_free(crl);
X509_STORE_free(store);
Expand Down Expand Up @@ -67697,6 +67810,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_TBS),

TEST_DECL(test_wolfSSL_X509_STORE_CTX),
TEST_DECL(test_X509_STORE_untrusted),
TEST_DECL(test_wolfSSL_X509_STORE_CTX_trusted_stack_cleanup),
TEST_DECL(test_wolfSSL_X509_STORE_CTX_get0_current_issuer),
TEST_DECL(test_wolfSSL_X509_STORE_set_flags),
Expand Down
1 change: 1 addition & 0 deletions wolfssl/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,7 @@ WOLFSSL_API WOLFSSL_STACK* wolfSSL_sk_new_node(void* heap);
WOLFSSL_API void wolfSSL_sk_free(WOLFSSL_STACK* sk);
WOLFSSL_API void wolfSSL_sk_free_node(WOLFSSL_STACK* in);
WOLFSSL_API WOLFSSL_STACK* wolfSSL_sk_dup(WOLFSSL_STACK* sk);
WOLFSSL_API WOLFSSL_STACK* wolfSSL_shallow_sk_dup(WOLFSSL_STACK* sk);
WOLFSSL_API int wolfSSL_sk_push_node(WOLFSSL_STACK** stack, WOLFSSL_STACK* in);
WOLFSSL_API WOLFSSL_STACK* wolfSSL_sk_get_node(WOLFSSL_STACK* sk, int idx);
WOLFSSL_API int wolfSSL_sk_push(WOLFSSL_STACK *st, const void *data);
Expand Down

0 comments on commit c92d258

Please sign in to comment.