X509 API: fix issues

1. BasicConstraints pathLenConstraint absent vs. 0 —
get_ext_d2i/set_ext/V3_EXT_d2i now distinguish "no constraint" from 0
per RFC 5280 §4.2.1.9, using the existing basicConstPlSet flag.
2. GENERAL_NAME_print GEN_DIRNAME — added missing return-value
normalization so the directory name is actually printed (was emitting
only DirName:).
3. GENERAL_NAME_print GEN_DNS — use ASN1_STRING_print like the EMAIL/URI
cases, avoiding NULL-strData deref and NUL-truncation.
4. X509_print BasicConstraints — print , pathlen:N to match OpenSSL.
5. X509_print Extended Key Usage — print Any Extended Key Usage (was
omitted).
6. get_ext_d2i CRL_DIST_OID double-free — null gn immediately after
ownership transfers to dp, so an error from the next push doesn't free
it twice.
7. X509V3_EXT_print SAN truncation/failure — match XSNPRINTF size cap to
the allocation; was truncating at indent==1 and failing at indent>=2.
8. X509V3_EXT_print AUTH_KEY/SUBJ_KEY NULL deref — NULL-check
i2s_ASN1_STRING return before passing to %s.
9. X509_add_ext SAN type confusion — reject DIRNAME/RID/X400/EDIPARTY;
only the ASN1_STRING*-backed types are read via gn->d.ia5. Was
performing a wild-pointer XMEMCPY in add_altname_ex.

Also: extracted the SAN and WOLFSSL_CUSTOM_OID arms of X509_add_ext into
static helpers (behavior-preserving).

Regression tests added for #1–5 and #9; existing GENERAL_NAME_print test
hardened (gives GEN_DIRNAME a real directoryName, eliminating an OOB
read that the print fix would otherwise expose).
This commit is contained in:
Sean Parkinson
2026-05-28 16:44:13 +10:00
parent b0d61c5e44
commit 8e4e76fdcc
4 changed files with 494 additions and 134 deletions
+201 -124
View File
@@ -945,10 +945,20 @@ WOLFSSL_X509_EXTENSION* wolfSSL_X509_set_ext(WOLFSSL_X509* x509, int loc)
WC_FREE_VAR_EX(cert, NULL, DYNAMIC_TYPE_DCERT);
return NULL;
}
a->length = (int)x509->pathLength;
/* RFC 5280 4.2.1.9: only populate pathLen when the
* pathLenConstraint field was actually present. When it is
* absent no limit is imposed, and pathlen must remain NULL so
* it is distinguishable from a pathLenConstraint of 0. */
if (x509->basicConstPlSet) {
a->length = (int)x509->pathLength;
/* Save ASN1_INTEGER in x509 extension */
ext->obj->pathlen = a;
/* Save ASN1_INTEGER in x509 extension */
ext->obj->pathlen = a;
}
else {
wolfSSL_ASN1_INTEGER_free(a);
ext->obj->pathlen = NULL;
}
ext->obj->ca = x509->isCa;
break;
@@ -1234,6 +1244,136 @@ static int asn1_string_copy_to_buffer(WOLFSSL_ASN1_STRING* str, byte** buf,
return WOLFSSL_SUCCESS;
}
/* Handle WC_NID_subject_alt_name for wolfSSL_X509_add_ext: iterate the
* GENERAL_NAME stack on the extension and add each entry to x509->altNames.
* Only types whose union member is WOLFSSL_ASN1_STRING* (DNS/RFC822/URI/IP/
* IA5) are read via gn->d.ia5; ASN_OTHER_TYPE is handled via SetOthername.
* Other types (DIRNAME/RID/X400/EDIPARTY) are rejected to avoid the
* type-confused union access that would XMEMCPY from a wild pointer in
* wolfSSL_X509_add_altname_ex. */
static int wolfssl_x509_add_subj_alt_name_ext(WOLFSSL_X509 *x509,
WOLFSSL_X509_EXTENSION *ext)
{
WOLFSSL_GENERAL_NAMES* gns = ext->ext_sk;
while (gns) {
WOLFSSL_GENERAL_NAME* gn = gns->data.gn;
if ((gn != NULL) && (gn->type == ASN_OTHER_TYPE)) {
char *buf = NULL;
int ret = 0;
word32 len = 0;
len = SetOthername(gn->d.otherName, NULL);
if (len == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
return WOLFSSL_FAILURE;
}
buf = (char*)XMALLOC(len, x509->heap, DYNAMIC_TYPE_X509_EXT);
if (buf == NULL) {
WOLFSSL_MSG("Couldn't allocate memory for othername");
return WOLFSSL_FAILURE;
}
/* SetOthername() cannot fail; already passed above. */
SetOthername(gn->d.otherName, (byte*)buf);
ret = wolfSSL_X509_add_altname_ex(x509, buf, len,
ASN_OTHER_TYPE);
XFREE(buf, x509->heap, DYNAMIC_TYPE_X509_EXT);
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
WOLFSSL_MSG("wolfSSL_X509_add_altname_ex() failed");
return WOLFSSL_FAILURE;
}
}
/* Only types whose union member are WOLFSSL_ASN1_STRING. */
else if (gn == NULL) {
WOLFSSL_MSG("Subject alternative name missing");
return WOLFSSL_FAILURE;
}
else {
if ((gn->type != ASN_RFC822_TYPE && gn->type != ASN_DNS_TYPE &&
gn->type != ASN_URI_TYPE && gn->type != ASN_IP_TYPE &&
gn->type != WOLFSSL_GEN_IA5) || (gn->d.ia5 == NULL)) {
WOLFSSL_MSG("Subject alternative name unsupported "
"GeneralName type or missing name");
return WOLFSSL_FAILURE;
}
if (wolfSSL_X509_add_altname_ex(x509, gn->d.ia5->data,
gn->d.ia5->length, gn->type) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("Failed to add subject alternative name");
return WOLFSSL_FAILURE;
}
}
gns = gns->next;
}
x509->subjAltNameSet = 1;
x509->subjAltNameCrit = (byte)ext->crit;
return WOLFSSL_SUCCESS;
}
#ifdef WOLFSSL_CUSTOM_OID
/* Handle the default (unrecognized NID) case of wolfSSL_X509_add_ext when
* custom-OID extensions are enabled: copy the extension OID text and value
* into the next free slot in x509->custom_exts, taking ownership of the
* allocations on success. */
static int wolfssl_x509_add_custom_ext(WOLFSSL_X509 *x509,
WOLFSSL_X509_EXTENSION *ext)
{
char *oid = NULL;
byte *val = NULL;
int err = 0;
if ((ext->obj == NULL) || (ext->value.length == 0) ||
(ext->value.data == NULL)) {
WOLFSSL_MSG("Extension has insufficient information.");
return WOLFSSL_FAILURE;
}
if ((x509->customExtCount < 0) ||
(x509->customExtCount >= NUM_CUSTOM_EXT)) {
WOLFSSL_MSG("Bad value for customExtCount.");
return WOLFSSL_FAILURE;
}
/* This is a viable custom extension. */
oid = (char*)XMALLOC(MAX_OID_STRING_SZ, x509->heap,
DYNAMIC_TYPE_X509_EXT);
val = (byte*)XMALLOC(ext->value.length, x509->heap,
DYNAMIC_TYPE_X509_EXT);
if ((oid == NULL) || (val == NULL)) {
WOLFSSL_MSG("Memory allocation failure.\n");
err = 1;
}
if (err == 0) {
XMEMCPY(val, ext->value.data, ext->value.length);
if (wolfSSL_OBJ_obj2txt(oid, MAX_OID_STRING_SZ, ext->obj, 1) < 0) {
err = 1;
}
}
if (err == 1) {
XFREE(val, x509->heap, DYNAMIC_TYPE_X509_EXT);
XFREE(oid, x509->heap, DYNAMIC_TYPE_X509_EXT);
return WOLFSSL_FAILURE;
}
/* ext->crit is WOLFSSL_ASN1_BOOLEAN */
if (ext->crit != 0 && ext->crit != -1) {
XFREE(val, x509->heap, DYNAMIC_TYPE_X509_EXT);
XFREE(oid, x509->heap, DYNAMIC_TYPE_X509_EXT);
return WOLFSSL_FAILURE;
}
/* x509->custom_exts now owns the buffers and they must be managed. */
x509->custom_exts[x509->customExtCount].oid = oid;
x509->custom_exts[x509->customExtCount].crit = (byte)ext->crit;
x509->custom_exts[x509->customExtCount].val = val;
x509->custom_exts[x509->customExtCount].valSz = ext->value.length;
x509->customExtCount++;
return WOLFSSL_SUCCESS;
}
#endif /* WOLFSSL_CUSTOM_OID */
int wolfSSL_X509_add_ext(WOLFSSL_X509 *x509, WOLFSSL_X509_EXTENSION *ext,
int loc)
{
@@ -1272,49 +1412,10 @@ int wolfSSL_X509_add_ext(WOLFSSL_X509 *x509, WOLFSSL_X509_EXTENSION *ext,
x509->subjKeyIdCrit = (byte)ext->crit;
break;
case WC_NID_subject_alt_name:
{
WOLFSSL_GENERAL_NAMES* gns = ext->ext_sk;
while (gns) {
WOLFSSL_GENERAL_NAME* gn = gns->data.gn;
if ((gn != NULL) && (gn->type == ASN_OTHER_TYPE)) {
char *buf = NULL;
int ret = 0;
word32 len = 0;
len = SetOthername(gn->d.otherName, NULL);
if (len == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
return WOLFSSL_FAILURE;
}
buf = (char*)XMALLOC(len, x509->heap, DYNAMIC_TYPE_X509_EXT);
if (buf == NULL) {
WOLFSSL_MSG("Couldn't allocate memory for othername");
return WOLFSSL_FAILURE;
}
/* SetOthername() cannot fail; already passed above. */
SetOthername(gn->d.otherName, (byte*)buf);
ret = wolfSSL_X509_add_altname_ex(x509, buf, len,
ASN_OTHER_TYPE);
XFREE(buf, x509->heap, DYNAMIC_TYPE_X509_EXT);
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
WOLFSSL_MSG("wolfSSL_X509_add_altname_ex() failed");
return WOLFSSL_FAILURE;
}
}
else if (!gn || !gn->d.ia5 ||
wolfSSL_X509_add_altname_ex(x509, gn->d.ia5->data,
gn->d.ia5->length, gn->type) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("Subject alternative name missing extension");
return WOLFSSL_FAILURE;
}
gns = gns->next;
if (wolfssl_x509_add_subj_alt_name_ext(x509, ext) != WOLFSSL_SUCCESS) {
return WOLFSSL_FAILURE;
}
x509->subjAltNameSet = 1;
x509->subjAltNameCrit = (byte)ext->crit;
break;
}
case WC_NID_key_usage:
if (ext && ext->value.data) {
if (ext->value.length == sizeof(word16)) {
@@ -1376,60 +1477,10 @@ int wolfSSL_X509_add_ext(WOLFSSL_X509 *x509, WOLFSSL_X509_EXTENSION *ext,
break;
default:
#ifdef WOLFSSL_CUSTOM_OID
{
char *oid = NULL;
byte *val = NULL;
int err = 0;
if ((ext->obj == NULL) || (ext->value.length == 0)) {
WOLFSSL_MSG("Extension has insufficient information.");
if (wolfssl_x509_add_custom_ext(x509, ext) != WOLFSSL_SUCCESS) {
return WOLFSSL_FAILURE;
}
if ((x509->customExtCount < 0) ||
(x509->customExtCount >= NUM_CUSTOM_EXT)) {
WOLFSSL_MSG("Bad value for customExtCount.");
return WOLFSSL_FAILURE;
}
/* This is a viable custom extension. */
oid = (char*)XMALLOC(MAX_OID_STRING_SZ, x509->heap,
DYNAMIC_TYPE_X509_EXT);
val = (byte*)XMALLOC(ext->value.length, x509->heap,
DYNAMIC_TYPE_X509_EXT);
if ((oid == NULL) || (val == NULL)) {
WOLFSSL_MSG("Memory allocation failure.\n");
err = 1;
}
if (err == 0) {
XMEMCPY(val, ext->value.data, ext->value.length);
if (wolfSSL_OBJ_obj2txt(oid, MAX_OID_STRING_SZ, ext->obj, 1) < 0) {
err = 1;
}
}
if (err == 1) {
XFREE(val, x509->heap, DYNAMIC_TYPE_X509_EXT);
XFREE(oid, x509->heap, DYNAMIC_TYPE_X509_EXT);
return WOLFSSL_FAILURE;
}
/* ext->crit is WOLFSSL_ASN1_BOOLEAN */
if (ext->crit != 0 && ext->crit != -1) {
XFREE(val, x509->heap, DYNAMIC_TYPE_X509_EXT);
XFREE(oid, x509->heap, DYNAMIC_TYPE_X509_EXT);
return WOLFSSL_FAILURE;
}
/* x509->custom_exts now owns the buffers and they must be managed. */
x509->custom_exts[x509->customExtCount].oid = oid;
x509->custom_exts[x509->customExtCount].crit = (byte)ext->crit;
x509->custom_exts[x509->customExtCount].val = val;
x509->custom_exts[x509->customExtCount].valSz = ext->value.length;
x509->customExtCount++;
break;
}
#else
WOLFSSL_MSG("Unsupported extension to add");
return WOLFSSL_FAILURE;
@@ -1529,9 +1580,9 @@ int wolfSSL_X509V3_EXT_print(WOLFSSL_BIO *out, WOLFSSL_X509_EXTENSION *ext,
WOLFSSL_MSG("Memory error");
return rc;
}
valLen = XSNPRINTF(val, (size_t)len, "%*s%s", indent, "",
str->strData);
if ((valLen < 0) || (valLen >= len)
valLen = XSNPRINTF(val, (size_t)len + indent,
"%*s%.*s", indent, "", str->length, str->data);
if ((valLen < 0) || (valLen >= len + indent)
|| ((tmpLen + valLen) >= tmpSz)) {
XFREE(val, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return rc;
@@ -1549,6 +1600,10 @@ int wolfSSL_X509V3_EXT_print(WOLFSSL_BIO *out, WOLFSSL_X509_EXTENSION *ext,
{
char* asn1str;
asn1str = wolfSSL_i2s_ASN1_STRING(NULL, str);
if (asn1str == NULL) {
WOLFSSL_MSG("wolfSSL_i2s_ASN1_STRING returned NULL");
return rc;
}
tmpLen = XSNPRINTF(tmp, tmpSz, "%*s%s", indent, "", asn1str);
XFREE(asn1str, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (tmpLen >= tmpSz)
@@ -1961,7 +2016,13 @@ void* wolfSSL_X509V3_EXT_d2i(WOLFSSL_X509_EXTENSION* ext)
}
/* Copy pathlen and CA into BASIC_CONSTRAINTS from object */
bc->ca = object->ca;
if (object->pathlen != NULL && object->pathlen->length > 0) {
/* RFC 5280 4.2.1.9:
* "A pathLenConstraint of zero indicates that no non-self-issued
* intermediate CA certificates may follow in a valid
* certification path."
* Check for length of 0 or greater.
*/
if ((object->pathlen != NULL) && (object->pathlen->length >= 0)) {
bc->pathlen = wolfSSL_ASN1_INTEGER_dup(object->pathlen);
if (bc->pathlen == NULL) {
WOLFSSL_MSG("Failed to duplicate ASN1_INTEGER");
@@ -2433,27 +2494,31 @@ void* wolfSSL_X509_get_ext_d2i(const WOLFSSL_X509* x509, int nid, int* c,
switch (nid) {
case BASIC_CA_OID:
if (x509->basicConstSet) {
WOLFSSL_ASN1_INTEGER* a;
bc = wolfSSL_BASIC_CONSTRAINTS_new();
if (!bc) {
WOLFSSL_MSG("wolfSSL_BASIC_CONSTRAINTS_new error");
return NULL;
}
a = wolfSSL_ASN1_INTEGER_new();
if (!a) {
WOLFSSL_MSG("wolfSSL_ASN1_INTEGER_new error");
wolfSSL_BASIC_CONSTRAINTS_free(bc);
return NULL;
}
a->length = (int)x509->pathLength;
#if defined(OPENSSL_ALL) || defined(WOLFSSL_QT) || \
defined(WOLFSSL_APACHE_HTTPD)
bc->ca = x509->isCa;
#endif
bc->pathlen = a;
/* RFC 5280 4.2.1.9: only populate pathLen when the
* pathLenConstraint field was present. When absent, no limit
* is imposed and pathlen must remain NULL so it is
* distinguishable from a pathLenConstraint of 0. */
if (x509->basicConstPlSet) {
WOLFSSL_ASN1_INTEGER* a = wolfSSL_ASN1_INTEGER_new();
if (a == NULL) {
WOLFSSL_MSG("wolfSSL_ASN1_INTEGER_new error");
wolfSSL_BASIC_CONSTRAINTS_free(bc);
return NULL;
}
a->length = (int)x509->pathLength;
bc->pathlen = a;
}
if (c != NULL) {
*c = x509->basicConstCrit;
}
@@ -2648,14 +2713,15 @@ void* wolfSSL_X509_get_ext_d2i(const WOLFSSL_X509* x509, int nid, int* c,
WOLFSSL_MSG("wolfSSL_sk_GENERAL_NAME_push error");
goto err;
}
/* gn now owned by stack. */
gn = NULL;
/* push DIST_POINT onto stack */
if (wolfSSL_sk_DIST_POINT_push(sk, dp) <= 0) {
WOLFSSL_MSG("Error pushing DIST_POINT onto stack");
goto err;
}
gn = NULL;
/* dp now owned by stack. */
dp = NULL;
}
@@ -5871,8 +5937,7 @@ int wolfSSL_GENERAL_NAME_print(WOLFSSL_BIO* out, WOLFSSL_GENERAL_NAME* gen)
case GEN_EMAIL:
ret = wolfSSL_BIO_printf(out, "email:");
ret = (ret > 0) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
if (ret == WOLFSSL_SUCCESS)
{
if (ret == WOLFSSL_SUCCESS) {
ret = wolfSSL_ASN1_STRING_print(out, gen->d.rfc822Name);
}
break;
@@ -5881,8 +5946,7 @@ int wolfSSL_GENERAL_NAME_print(WOLFSSL_BIO* out, WOLFSSL_GENERAL_NAME* gen)
ret = wolfSSL_BIO_printf(out, "DNS:");
ret = (ret > 0) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
if (ret == WOLFSSL_SUCCESS) {
ret = wolfSSL_BIO_printf(out, "%s", gen->d.dNSName->strData);
ret = (ret > 0) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
ret = wolfSSL_ASN1_STRING_print(out, gen->d.dNSName);
}
break;
@@ -5893,6 +5957,7 @@ int wolfSSL_GENERAL_NAME_print(WOLFSSL_BIO* out, WOLFSSL_GENERAL_NAME* gen)
case GEN_DIRNAME:
ret = wolfSSL_BIO_printf(out, "DirName:");
ret = (ret > 0) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
if (ret == WOLFSSL_SUCCESS) {
ret = wolfSSL_X509_NAME_print_ex(out, gen->d.directoryName, 0,
XN_FLAG_ONELINE);
@@ -7094,7 +7159,8 @@ static int X509PrintExtendedKeyUsage(WOLFSSL_BIO* bio, WOLFSSL_X509* x509,
EXTKEYUSE_EMAILPROT,
EXTKEYUSE_CODESIGN,
EXTKEYUSE_CLIENT_AUTH,
EXTKEYUSE_SERVER_AUTH
EXTKEYUSE_SERVER_AUTH,
EXTKEYUSE_ANY
};
const char* usageStrs[] = {
"OCSP Signing",
@@ -7102,7 +7168,8 @@ static int X509PrintExtendedKeyUsage(WOLFSSL_BIO* bio, WOLFSSL_X509* x509,
"E-mail Protection",
"Code Signing",
"TLS Web Client Authentication",
"TLS Web Server Authentication"
"TLS Web Server Authentication",
"Any Extended Key Usage"
};
if (bio == NULL || x509 == NULL) {
@@ -7483,12 +7550,22 @@ static int X509PrintExtensions(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, int indent)
ret = WOLFSSL_FAILURE;
break;
}
if ((scratchLen = XSNPRINTF(
scratch, scratchSz,
"%*sCA:%s\n",
indent + 8, "", (x509->isCa)? "TRUE": "FALSE"))
>= scratchSz)
{
/* Match OpenSSL output: print "CA:TRUE"/"CA:FALSE" and append
* ", pathlen:N" only when a pathLenConstraint is present
* (RFC 5280 4.2.1.9). An absent constraint imposes no limit
* and must not be shown as pathlen:0. */
if (x509->basicConstPlSet) {
scratchLen = XSNPRINTF(scratch, scratchSz,
"%*sCA:%s, pathlen:%u\n",
indent + 8, "", (x509->isCa) ? "TRUE" : "FALSE",
(unsigned int)x509->pathLength);
}
else {
scratchLen = XSNPRINTF(scratch, scratchSz,
"%*sCA:%s\n",
indent + 8, "", (x509->isCa) ? "TRUE" : "FALSE");
}
if (scratchLen >= scratchSz) {
ret = WOLFSSL_FAILURE;
break;
}
+125 -4
View File
@@ -20566,6 +20566,7 @@ static int test_wolfSSL_GENERAL_NAME_print(void)
ACCESS_DESCRIPTION* ad = NULL;
ASN1_IA5STRING *dnsname = NULL;
ASN1_OBJECT* ridObj = NULL;
X509_NAME* dirName = NULL;
const unsigned char v4Addr[] = {192,168,53,1};
const unsigned char v6Addr[] =
@@ -20820,22 +20821,35 @@ static int test_wolfSSL_GENERAL_NAME_print(void)
/* test for GEN_DIRNAME */
ExpectNotNull(gn = wolfSSL_GENERAL_NAME_new());
/* Build a real directoryName (X509_NAME) so the print path exercises
* wolfSSL_X509_NAME_print_ex on a valid object. Forcing the type without
* setting d.directoryName would leave it aliasing the default IA5 string
* and cause an out-of-bounds read when printed. */
ExpectNotNull(dirName = X509_NAME_new());
ExpectIntEQ(X509_NAME_add_entry_by_NID(dirName, NID_commonName,
MBSTRING_UTF8, (unsigned char*)"wolfSSLDirNameTest", -1, -1, 0), 1);
if (gn != NULL) {
/* Replace the default IA5 string allocated by GENERAL_NAME_new with
* the directoryName and take ownership of it. */
wolfSSL_ASN1_STRING_free(gn->d.ia5);
gn->type = GEN_DIRNAME;
gn->d.directoryName = dirName;
dirName = NULL; /* gn owns it now; freed by GENERAL_NAME_free */
}
ExpectIntEQ(GENERAL_NAME_print(out, gn), 1);
XMEMSET(outbuf,0,sizeof(outbuf));
ExpectIntGT(BIO_read(out, outbuf, sizeof(outbuf)), 0);
/* Output must start with the label and contain the directory name. */
ExpectIntEQ(XSTRNCMP((const char*)outbuf, dirNameStr, XSTRLEN(dirNameStr)),
0);
ExpectNotNull(XSTRSTR((const char*)outbuf, "wolfSSLDirNameTest"));
/* Duplicating GEN_DIRNAME not supported. */
ExpectNull(dup_gn = GENERAL_NAME_dup(gn));
/* Restore to GEN_IA5 (default) to avoid memory leak. */
if (gn != NULL) {
gn->type = GEN_IA5;
}
GENERAL_NAME_free(gn);
gn = NULL;
/* Only freed here if ownership was not transferred (e.g. gn alloc failed). */
X509_NAME_free(dirName);
dirName = NULL;
/* test for GEN_RID */
p = ridData;
@@ -26974,6 +26988,111 @@ static int test_wolfSSL_X509_print(void)
return EXPECT_RESULT();
}
static int test_wolfSSL_X509_print_basic_constraints(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \
!defined(NO_RSA) && defined(XSNPRINTF) && !defined(WC_DISABLE_RADIX_ZERO_PAD)
/* X509_print must match OpenSSL's Basic Constraints output: "CA:TRUE"
* alone when no pathLenConstraint is present, and "CA:TRUE, pathlen:N"
* when one is (including the meaningful value 0). */
struct {
const char* file;
const char* expect; /* substring that must be present */
const char* absent; /* substring that must be absent, or NULL */
} cases[] = {
{ "./certs/ca-cert.pem", "CA:TRUE", "pathlen" },
{ "./certs/intermediate/ca-int-cert.pem", "CA:TRUE, pathlen:1", NULL },
{ "./certs/test-pathlen/chainG-ICA1-pathlen0.pem",
"CA:TRUE, pathlen:0", NULL },
};
size_t i;
for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
X509* x509 = NULL;
BIO* bio = NULL;
char* data = NULL;
int len = 0;
char buf[8192];
ExpectNotNull(x509 = X509_load_certificate_file(cases[i].file,
WOLFSSL_FILETYPE_PEM));
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
ExpectIntEQ(X509_print(bio, x509), SSL_SUCCESS);
/* Memory BIO data is not NUL-terminated; copy into a bounded buffer. */
ExpectIntGT((len = BIO_get_mem_data(bio, &data)), 0);
ExpectIntLT(len, (int)sizeof(buf));
if ((data != NULL) && (len > 0) && (len < (int)sizeof(buf))) {
XMEMCPY(buf, data, (size_t)len);
buf[len] = '\0';
ExpectNotNull(XSTRSTR(buf, cases[i].expect));
if (cases[i].absent != NULL) {
ExpectNull(XSTRSTR(buf, cases[i].absent));
}
}
BIO_free(bio);
X509_free(x509);
}
#endif
return EXPECT_RESULT();
}
static int test_wolfSSL_X509_print_ext_key_usage(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \
!defined(NO_RSA) && defined(XSNPRINTF) && !defined(WC_DISABLE_RADIX_ZERO_PAD)
/* Self-signed RSA cert (CN="wolfSSL anyEKU test", valid until 2126) whose
* only Extended Key Usage is anyExtendedKeyUsage (OID 2.5.29.37.0).
* X509_print must render it as "Any Extended Key Usage" to match OpenSSL,
* rather than omitting it. */
static const char anyEkuCertPem[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIDMDCCAhigAwIBAgIUGFipYIiuuQZHNjsi0R8t6nZrFVowDQYJKoZIhvcNAQEL\n"
"BQAwHjEcMBoGA1UEAwwTd29sZlNTTCBhbnlFS1UgdGVzdDAgFw0yNjA1MjgwMTI5\n"
"MjBaGA8yMTI2MDUwNDAxMjkyMFowHjEcMBoGA1UEAwwTd29sZlNTTCBhbnlFS1Ug\n"
"dGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKIXS15QjpWlqpqk\n"
"sUTD/mgm7akIZfp2DVoweJsf8BC/tnwMX1noWUFjBC8SLOHyhx4bmWxVBzFXv/uu\n"
"ICoUCq63pgnxj3rQbPAa1pwAX4UsYS1PS/2mO2o7lRLRdDLIaBXuUClVHGjti9x5\n"
"x5++4FFhOhKNt7CkYVAfXasTFKdZqSnKdlYX2rM8LoCjP3YHIC5jEIgjyNUEqzup\n"
"Ls02dIaAly5O8yzasUtULwE76E/UpyhE+o2dkhxnc6ukqU5CBLeHNOUJgvThW1lS\n"
"SlGXl8Kd5uvBvDflXb6y3TQBEY/hb40JzYeH+hHf4YIqCtvfy6PMA+Rcu2CKlDpP\n"
"FVf85pUCAwEAAaNkMGIwHQYDVR0OBBYEFAkmMlYp9K3sbeQ8/RluOviXZ3JcMB8G\n"
"A1UdIwQYMBaAFAkmMlYp9K3sbeQ8/RluOviXZ3JcMA8GA1UdEwEB/wQFMAMBAf8w\n"
"DwYDVR0lBAgwBgYEVR0lADANBgkqhkiG9w0BAQsFAAOCAQEAHX5wAZDAyFSmmsn4\n"
"Mu7TayCx+VbcBgvL4ZdxxJYXexslzI8OviKSgpC56LaVB5JpNqZtFS4pZZikG7nv\n"
"kYCoiU33XN82/gggq1bv8rKO740V6kGev3gfEeJuTX30fCPZ18znE1fU8+VQba1L\n"
"bPn0h746Ivom47/1VMg6y7CbTJg90+lloPWcTWujYfm5jWychqWurhfZmAYcUlBH\n"
"Ksk0l4kiEJA6lSnPp3MqBS0GzwsCixSqYc1W1TlwNGNg38cLP2Z5jerJZlazuVws\n"
"SeEbYO6TIhsy6QJy0Pd7hu9DUOKRxp+OQubL6WgWpjrGl1LUzH5sI5pyWueEAEBu\n"
"e74xbw==\n"
"-----END CERTIFICATE-----\n";
X509* x509 = NULL;
BIO* bio = NULL;
char* data = NULL;
int len = 0;
char buf[8192];
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_buffer(
(const unsigned char*)anyEkuCertPem, (int)XSTRLEN(anyEkuCertPem),
WOLFSSL_FILETYPE_PEM));
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
ExpectIntEQ(X509_print(bio, x509), SSL_SUCCESS);
/* Memory BIO data is not NUL-terminated; copy into a bounded buffer. */
ExpectIntGT((len = BIO_get_mem_data(bio, &data)), 0);
ExpectIntLT(len, (int)sizeof(buf));
if ((data != NULL) && (len > 0) && (len < (int)sizeof(buf))) {
XMEMCPY(buf, data, (size_t)len);
buf[len] = '\0';
ExpectNotNull(XSTRSTR(buf, "X509v3 Extended Key Usage"));
ExpectNotNull(XSTRSTR(buf, "Any Extended Key Usage"));
}
BIO_free(bio);
X509_free(x509);
#endif
return EXPECT_RESULT();
}
static int test_wolfSSL_X509_CRL_print(void)
{
EXPECT_DECLS;
@@ -40367,6 +40486,8 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_X509_CRL),
#ifndef NO_BIO
TEST_DECL(test_wolfSSL_X509_print),
TEST_DECL(test_wolfSSL_X509_print_basic_constraints),
TEST_DECL(test_wolfSSL_X509_print_ext_key_usage),
TEST_DECL(test_wolfSSL_X509_CRL_print),
#endif
+162 -6
View File
@@ -593,6 +593,70 @@ int test_wolfSSL_X509_add_ext(void)
return EXPECT_RESULT();
}
int test_wolfSSL_X509_add_ext_dirname_san_rejected(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_ALL) && !defined(NO_RSA)
WOLFSSL_X509* x509 = NULL;
WOLFSSL_X509_EXTENSION* ext = NULL;
WOLFSSL_ASN1_OBJECT* obj = NULL;
WOLFSSL_GENERAL_NAME* gn = NULL;
WOLFSSL_X509_NAME* dirName = NULL;
WOLFSSL_STACK* sk = NULL;
ExpectNotNull(x509 = wolfSSL_X509_new());
ExpectNotNull(ext = wolfSSL_X509_EXTENSION_new());
/* Build a GEN_DIRNAME GENERAL_NAME with a real directoryName so that
* gn->d.directoryName aliases an X509_NAME object via the union. */
ExpectNotNull(gn = wolfSSL_GENERAL_NAME_new());
ExpectNotNull(dirName = wolfSSL_X509_NAME_new());
ExpectIntEQ(wolfSSL_X509_NAME_add_entry_by_NID(dirName, NID_commonName,
MBSTRING_UTF8, (unsigned char*)"dirname-san-test", -1, -1, 0), 1);
if (gn != NULL) {
/* Drop the default IA5 string and install the X509_NAME. */
wolfSSL_ASN1_STRING_free(gn->d.ia5);
gn->type = GEN_DIRNAME;
gn->d.directoryName = dirName;
dirName = NULL; /* gn owns the X509_NAME now */
}
/* Build the ext: SAN OID + ext_sk containing the DirName GENERAL_NAME. */
ExpectNotNull(sk = wolfSSL_sk_new_null());
if (sk != NULL) {
sk->type = STACK_TYPE_GEN_NAME;
}
ExpectIntGT(wolfSSL_sk_GENERAL_NAME_push(sk, gn), 0);
gn = NULL; /* sk owns gn now */
ExpectNotNull(obj = wolfSSL_OBJ_nid2obj(NID_subject_alt_name));
if (obj != NULL) {
obj->type = NID_subject_alt_name;
obj->nid = NID_subject_alt_name;
}
if ((ext != NULL) && (obj != NULL) && (sk != NULL)) {
ext->obj = obj;
obj = NULL; /* ext owns obj now */
ext->ext_sk = sk;
sk = NULL; /* ext owns sk now */
}
/* The unsupported GeneralName type must be rejected safely, NOT crash
* or read OOB via a type-confused d.ia5 dereference. */
ExpectIntEQ(wolfSSL_X509_add_ext(x509, ext, -1),
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
/* Cleanup. The success-path owners (set to NULL above) are no-ops. */
wolfSSL_ASN1_OBJECT_free(obj);
wolfSSL_sk_GENERAL_NAME_pop_free(sk, wolfSSL_GENERAL_NAME_free);
wolfSSL_GENERAL_NAME_free(gn);
wolfSSL_X509_NAME_free(dirName);
wolfSSL_X509_EXTENSION_free(ext);
wolfSSL_X509_free(x509);
#endif
return EXPECT_RESULT();
}
int test_wolfSSL_X509_get_ext_count(void)
{
EXPECT_DECLS;
@@ -1098,10 +1162,6 @@ int test_wolfSSL_X509V3_EXT_bc(void)
ExpectNotNull(ext = wolfSSL_X509_EXTENSION_new());
ExpectNotNull(obj = wolfSSL_ASN1_OBJECT_new());
ExpectNotNull(pathLen = wolfSSL_ASN1_INTEGER_new());
if (pathLen != NULL) {
pathLen->length = 2;
}
if (obj != NULL) {
obj->type = NID_basic_constraints;
@@ -1109,17 +1169,47 @@ int test_wolfSSL_X509V3_EXT_bc(void)
}
ExpectIntEQ(wolfSSL_X509_EXTENSION_set_object(ext, obj), WOLFSSL_SUCCESS);
ExpectNotNull(wolfSSL_X509V3_EXT_get(ext));
/* No pathlen set. */
/* No pathLenConstraint present. Per RFC 5280 4.2.1.9 no limit is imposed,
* so pathlen must be NULL (and distinguishable from a value of 0). */
ExpectNotNull(bc = (WOLFSSL_BASIC_CONSTRAINTS*)wolfSSL_X509V3_EXT_d2i(ext));
ExpectNull(bc->pathlen);
wolfSSL_BASIC_CONSTRAINTS_free(bc);
bc = NULL;
/* pathLenConstraint of 0 is valid and meaningful (the CA may only issue
* end-entity certificates). It must be preserved, not conflated with an
* absent constraint. */
ExpectNotNull(pathLen = wolfSSL_ASN1_INTEGER_new());
if (pathLen != NULL) {
pathLen->length = 0;
}
if ((ext != NULL) && (ext->obj != NULL)) {
ext->obj->pathlen = pathLen;
pathLen = NULL;
}
/* pathlen set. */
ExpectNotNull(bc = (WOLFSSL_BASIC_CONSTRAINTS*)wolfSSL_X509V3_EXT_d2i(ext));
ExpectNotNull(bc->pathlen);
ExpectIntEQ(bc->pathlen->length, 0);
wolfSSL_BASIC_CONSTRAINTS_free(bc);
bc = NULL;
/* A non-zero pathLenConstraint is preserved as-is. */
if ((ext != NULL) && (ext->obj != NULL)) {
wolfSSL_ASN1_INTEGER_free(ext->obj->pathlen);
ext->obj->pathlen = NULL;
}
ExpectNotNull(pathLen = wolfSSL_ASN1_INTEGER_new());
if (pathLen != NULL) {
pathLen->length = 2;
}
if ((ext != NULL) && (ext->obj != NULL)) {
ext->obj->pathlen = pathLen;
pathLen = NULL;
}
ExpectNotNull(bc = (WOLFSSL_BASIC_CONSTRAINTS*)wolfSSL_X509V3_EXT_d2i(ext));
ExpectNotNull(bc->pathlen);
ExpectIntEQ(bc->pathlen->length, 2);
wolfSSL_ASN1_INTEGER_free(pathLen);
wolfSSL_BASIC_CONSTRAINTS_free(bc);
@@ -1129,6 +1219,72 @@ int test_wolfSSL_X509V3_EXT_bc(void)
return EXPECT_RESULT();
}
int test_wolfSSL_X509_get_ext_d2i_basic_constraints(void)
{
EXPECT_DECLS;
#if !defined(NO_FILESYSTEM) && defined(OPENSSL_ALL) && !defined(NO_RSA)
XFILE f = XBADFILE;
WOLFSSL_X509* x509 = NULL;
WOLFSSL_BASIC_CONSTRAINTS* bc = NULL;
int crit = 0;
/* CA certificate with basicConstraints CA:TRUE and *no* pathLenConstraint.
* Per RFC 5280 4.2.1.9 no path length limit is imposed, so the returned
* pathlen must be NULL - it must not be reported as a value of 0. */
ExpectTrue((f = XFOPEN("./certs/ca-cert.pem", "rb")) != XBADFILE);
ExpectNotNull(x509 = wolfSSL_PEM_read_X509(f, NULL, NULL, NULL));
if (f != XBADFILE) {
XFCLOSE(f);
f = XBADFILE;
}
ExpectNotNull(bc = (WOLFSSL_BASIC_CONSTRAINTS*)wolfSSL_X509_get_ext_d2i(
x509, NID_basic_constraints, &crit, NULL));
ExpectNull(bc->pathlen);
wolfSSL_BASIC_CONSTRAINTS_free(bc);
bc = NULL;
wolfSSL_X509_free(x509);
x509 = NULL;
/* Intermediate CA with basicConstraints CA:TRUE, pathlen:1. */
ExpectTrue((f = XFOPEN("./certs/intermediate/ca-int-cert.pem", "rb")) !=
XBADFILE);
ExpectNotNull(x509 = wolfSSL_PEM_read_X509(f, NULL, NULL, NULL));
if (f != XBADFILE) {
XFCLOSE(f);
f = XBADFILE;
}
ExpectNotNull(bc = (WOLFSSL_BASIC_CONSTRAINTS*)wolfSSL_X509_get_ext_d2i(
x509, NID_basic_constraints, &crit, NULL));
ExpectNotNull(bc->pathlen);
ExpectIntEQ(bc->pathlen->length, 1);
wolfSSL_BASIC_CONSTRAINTS_free(bc);
bc = NULL;
wolfSSL_X509_free(x509);
x509 = NULL;
/* CA with basicConstraints CA:TRUE, pathlen:0. A pathLenConstraint of 0 is
* valid and meaningful (the CA may only issue end-entity certificates) and
* must be reported (non-NULL pathlen, value 0) - it must not be conflated
* with an absent constraint. */
ExpectTrue((f = XFOPEN("./certs/test-pathlen/chainG-ICA1-pathlen0.pem",
"rb")) != XBADFILE);
ExpectNotNull(x509 = wolfSSL_PEM_read_X509(f, NULL, NULL, NULL));
if (f != XBADFILE) {
XFCLOSE(f);
f = XBADFILE;
}
ExpectNotNull(bc = (WOLFSSL_BASIC_CONSTRAINTS*)wolfSSL_X509_get_ext_d2i(
x509, NID_basic_constraints, &crit, NULL));
ExpectNotNull(bc->pathlen);
ExpectIntEQ(bc->pathlen->length, 0);
wolfSSL_BASIC_CONSTRAINTS_free(bc);
bc = NULL;
wolfSSL_X509_free(x509);
x509 = NULL;
#endif
return EXPECT_RESULT();
}
int test_wolfSSL_X509V3_EXT_san(void)
{
EXPECT_DECLS;
+6
View File
@@ -30,6 +30,7 @@ int test_wolfSSL_X509_get_ext_by_NID(void);
int test_wolfSSL_X509_get_ext_subj_alt_name(void);
int test_wolfSSL_X509_set_ext(void);
int test_wolfSSL_X509_add_ext(void);
int test_wolfSSL_X509_add_ext_dirname_san_rejected(void);
int test_wolfSSL_X509_get_ext_count(void);
int test_wolfSSL_X509_stack_extensions(void);
int test_wolfSSL_X509_EXTENSION_new(void);
@@ -42,6 +43,7 @@ int test_wolfSSL_X509V3_set_ctx(void);
int test_wolfSSL_X509V3_EXT_get(void);
int test_wolfSSL_X509V3_EXT_nconf(void);
int test_wolfSSL_X509V3_EXT_bc(void);
int test_wolfSSL_X509_get_ext_d2i_basic_constraints(void);
int test_wolfSSL_X509V3_EXT_san(void);
int test_wolfSSL_X509V3_EXT_aia(void);
int test_wolfSSL_X509V3_EXT(void);
@@ -62,6 +64,8 @@ int test_wolfSSL_NAME_CONSTRAINTS_excluded(void);
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509_get_ext_subj_alt_name), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509_set_ext), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509_add_ext), \
TEST_DECL_GROUP("ossl_x509_ext", \
test_wolfSSL_X509_add_ext_dirname_san_rejected), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509_get_ext_count), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509_stack_extensions), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509_EXTENSION_new), \
@@ -76,6 +80,8 @@ int test_wolfSSL_NAME_CONSTRAINTS_excluded(void);
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_get), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_nconf), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_bc), \
TEST_DECL_GROUP("ossl_x509_ext", \
test_wolfSSL_X509_get_ext_d2i_basic_constraints), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_san), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_aia), \
TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT), \