Skip to content

Commit d278da0

Browse files
authored
Merge pull request #10112 from embhorn/zd21470
Fix CertFromX509 copy length check
2 parents ed1f055 + 2b1cde5 commit d278da0

3 files changed

Lines changed: 264 additions & 16 deletions

File tree

src/x509.c

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11870,25 +11870,28 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
1187011870
return WOLFSSL_FAILURE;
1187111871
}
1187211872

11873-
if (x509->authKeyIdSz < sizeof(cert->akid)) {
1187411873
#ifdef WOLFSSL_AKID_NAME
11875-
cert->rawAkid = 0;
11876-
if (x509->authKeyIdSrc) {
11877-
XMEMCPY(cert->akid, x509->authKeyIdSrc, x509->authKeyIdSrcSz);
11878-
cert->akidSz = (int)x509->authKeyIdSrcSz;
11879-
cert->rawAkid = 1;
11874+
cert->rawAkid = 0;
11875+
if (x509->authKeyIdSrc) {
11876+
if (x509->authKeyIdSrcSz > sizeof(cert->akid)) {
11877+
WOLFSSL_MSG("Auth Key ID too large");
11878+
WOLFSSL_ERROR_VERBOSE(BUFFER_E);
11879+
return WOLFSSL_FAILURE;
1188011880
}
11881-
else
11881+
XMEMCPY(cert->akid, x509->authKeyIdSrc, x509->authKeyIdSrcSz);
11882+
cert->akidSz = (int)x509->authKeyIdSrcSz;
11883+
cert->rawAkid = 1;
11884+
}
11885+
else
1188211886
#endif
11883-
if (x509->authKeyId) {
11884-
XMEMCPY(cert->akid, x509->authKeyId, x509->authKeyIdSz);
11885-
cert->akidSz = (int)x509->authKeyIdSz;
11887+
if (x509->authKeyId) {
11888+
if (x509->authKeyIdSz > sizeof(cert->akid)) {
11889+
WOLFSSL_MSG("Auth Key ID too large");
11890+
WOLFSSL_ERROR_VERBOSE(BUFFER_E);
11891+
return WOLFSSL_FAILURE;
1188611892
}
11887-
}
11888-
else {
11889-
WOLFSSL_MSG("Auth Key ID too large");
11890-
WOLFSSL_ERROR_VERBOSE(BUFFER_E);
11891-
return WOLFSSL_FAILURE;
11893+
XMEMCPY(cert->akid, x509->authKeyId, x509->authKeyIdSz);
11894+
cert->akidSz = (int)x509->authKeyIdSz;
1189211895
}
1189311896

1189411897
for (i = 0; i < x509->certPoliciesNb; i++) {

tests/api/test_x509.c

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
#include <wolfssl/internal.h>
4040
#include <wolfssl/wolfcrypt/asn.h>
41+
#include <wolfssl/wolfcrypt/asn_public.h>
4142

4243
#if defined(OPENSSL_ALL) && \
4344
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
@@ -632,3 +633,245 @@ int test_x509_time_field_overread_via_tls(void)
632633
#endif /* compile guards */
633634
return EXPECT_RESULT();
634635
}
636+
637+
638+
/* Test that CertFromX509 rejects an oversized raw AuthorityKeyIdentifier
639+
* extension. Before the fix, the guard checked authKeyIdSz (the [0]
640+
* keyIdentifier sub-field) but the WOLFSSL_AKID_NAME branch copied
641+
* authKeyIdSrcSz (the full extension) bytes, causing a heap overflow. */
642+
int test_x509_CertFromX509_akid_overflow(void)
643+
{
644+
EXPECT_DECLS;
645+
#if defined(WOLFSSL_AKID_NAME) && defined(WOLFSSL_CERT_GEN) && \
646+
defined(WOLFSSL_CERT_EXT) && !defined(NO_BIO) && \
647+
(defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL))
648+
/* DER builder helpers -- write into a flat buffer */
649+
#ifdef WOLFSSL_SMALL_STACK
650+
unsigned char* buf = NULL;
651+
#else
652+
unsigned char buf[16384];
653+
#endif
654+
size_t pos = 0;
655+
size_t akid_val_len;
656+
unsigned char* akid_val = NULL;
657+
WOLFSSL_X509* x = NULL;
658+
WOLFSSL_BIO* bio = NULL;
659+
660+
#ifdef WOLFSSL_SMALL_STACK
661+
buf = (unsigned char*)XMALLOC(16384, NULL, DYNAMIC_TYPE_TMP_BUFFER);
662+
ExpectNotNull(buf);
663+
if (buf == NULL)
664+
return EXPECT_RESULT();
665+
#endif
666+
667+
#define PUT1(b) do { buf[pos++] = (b); } while(0)
668+
#define PUTN(p, n) do { XMEMCPY(buf + pos, (p), (n)); pos += (n); } while(0)
669+
670+
/* Emit tag + definite-length header, return header size */
671+
#define TLV_HDR(tag, n, out, hlen) do { \
672+
size_t _i = 0; \
673+
(out)[_i++] = (tag); \
674+
if ((n) < 0x80u) { (out)[_i++] = (unsigned char)(n); } \
675+
else if ((n) < 0x100u) { (out)[_i++] = 0x81; \
676+
(out)[_i++] = (unsigned char)(n); } \
677+
else if ((n) < 0x10000u) { (out)[_i++] = 0x82; \
678+
(out)[_i++] = (unsigned char)((n)>>8); \
679+
(out)[_i++] = (unsigned char)(n); } \
680+
(hlen) = _i; \
681+
} while(0)
682+
683+
/* Wrap [start, pos) in-place with a TLV header */
684+
#define WRAP(start, tag) do { \
685+
size_t _len = pos - (start); \
686+
unsigned char _hdr[6]; size_t _hlen; \
687+
TLV_HDR((tag), _len, _hdr, _hlen); \
688+
XMEMMOVE(buf + (start) + _hlen, buf + (start), _len); \
689+
XMEMCPY(buf + (start), _hdr, _hlen); \
690+
pos += _hlen; \
691+
} while(0)
692+
693+
/* ---- Build AKID extension value ---- */
694+
{
695+
size_t akid_start = pos;
696+
size_t s;
697+
int i;
698+
699+
/* [0] keyIdentifier: 20 bytes (small, passes old check) */
700+
s = pos;
701+
for (i = 0; i < 20; i++) PUT1(0x41);
702+
WRAP(s, 0x80);
703+
704+
/* [1] authorityCertIssuer: one URI of ~4000 bytes
705+
* This makes authKeyIdSrcSz >> sizeof(cert->akid) (~1628) */
706+
s = pos;
707+
{
708+
const char* pfx = "http://e/";
709+
PUTN(pfx, (size_t)XSTRLEN(pfx));
710+
for (i = 0; i < 4000; i++) PUT1('Z');
711+
}
712+
WRAP(s, 0x86); /* GeneralName [6] URI */
713+
WRAP(s, 0xA1); /* [1] IMPLICIT */
714+
715+
/* [2] authorityCertSerialNumber */
716+
s = pos;
717+
PUT1(0x01);
718+
WRAP(s, 0x82);
719+
720+
WRAP(akid_start, 0x30); /* SEQUENCE */
721+
akid_val_len = pos - akid_start;
722+
akid_val = (unsigned char*)XMALLOC(akid_val_len, NULL,
723+
DYNAMIC_TYPE_TMP_BUFFER);
724+
ExpectNotNull(akid_val);
725+
if (akid_val != NULL)
726+
XMEMCPY(akid_val, buf + akid_start, akid_val_len);
727+
}
728+
729+
/* ---- Build minimal self-signed v3 certificate ---- */
730+
pos = 0;
731+
{
732+
size_t tbs_start = pos;
733+
size_t s;
734+
735+
/* version [0] EXPLICIT INTEGER 2 (v3) */
736+
PUT1(0xA0); PUT1(0x03); PUT1(0x02); PUT1(0x01); PUT1(0x02);
737+
738+
/* serialNumber INTEGER 1 */
739+
PUT1(0x02); PUT1(0x01); PUT1(0x01);
740+
741+
/* signature: ecdsa-with-SHA256 */
742+
s = pos;
743+
{
744+
unsigned char oid[] = {0x06,0x08,0x2A,0x86,0x48,0xCE,
745+
0x3D,0x04,0x03,0x02};
746+
PUTN(oid, sizeof(oid));
747+
}
748+
WRAP(s, 0x30);
749+
750+
/* issuer: CN=A */
751+
s = pos;
752+
{
753+
size_t rdn = pos, atv = pos;
754+
unsigned char cn[] = {0x06,0x03,0x55,0x04,0x03};
755+
PUTN(cn, sizeof(cn));
756+
PUT1(0x0C); PUT1(0x01); PUT1('A');
757+
WRAP(atv, 0x30); WRAP(rdn, 0x31); WRAP(s, 0x30);
758+
}
759+
760+
/* validity */
761+
s = pos;
762+
{
763+
unsigned char t1[] = {0x17,0x0D,'2','5','0','1','0','1',
764+
'0','0','0','0','0','0','Z'};
765+
unsigned char t2[] = {0x17,0x0D,'3','5','0','1','0','1',
766+
'0','0','0','0','0','0','Z'};
767+
PUTN(t1, sizeof(t1)); PUTN(t2, sizeof(t2));
768+
}
769+
WRAP(s, 0x30);
770+
771+
/* subject: CN=A */
772+
s = pos;
773+
{
774+
size_t rdn = pos, atv = pos;
775+
unsigned char cn[] = {0x06,0x03,0x55,0x04,0x03};
776+
PUTN(cn, sizeof(cn));
777+
PUT1(0x0C); PUT1(0x01); PUT1('A');
778+
WRAP(atv, 0x30); WRAP(rdn, 0x31); WRAP(s, 0x30);
779+
}
780+
781+
/* subjectPublicKeyInfo: EC P-256 with dummy point */
782+
s = pos;
783+
{
784+
size_t alg = pos, bs;
785+
unsigned char ecpk[] = {0x06,0x07,0x2A,0x86,0x48,0xCE,
786+
0x3D,0x02,0x01};
787+
unsigned char p256[] = {0x06,0x08,0x2A,0x86,0x48,0xCE,
788+
0x3D,0x03,0x01,0x07};
789+
PUTN(ecpk, sizeof(ecpk));
790+
PUTN(p256, sizeof(p256));
791+
WRAP(alg, 0x30);
792+
bs = pos;
793+
PUT1(0x00); PUT1(0x04);
794+
/* Use P-256 generator point (valid on-curve point) so that
795+
* builds with WOLFSSL_VALIDATE_ECC_IMPORT accept the key. */
796+
{
797+
static const unsigned char p256G[64] = {
798+
0x6B,0x17,0xD1,0xF2,0xE1,0x2C,0x42,0x47,
799+
0xF8,0xBC,0xE6,0xE5,0x63,0xA4,0x40,0xF2,
800+
0x77,0x03,0x7D,0x81,0x2D,0xEB,0x33,0xA0,
801+
0xF4,0xA1,0x39,0x45,0xD8,0x98,0xC2,0x96,
802+
0x4F,0xE3,0x42,0xE2,0xFE,0x1A,0x7F,0x9B,
803+
0x8E,0xE7,0xEB,0x4A,0x7C,0x0F,0x9E,0x16,
804+
0x2B,0xCE,0x33,0x57,0x6B,0x31,0x5E,0xCE,
805+
0xCB,0xB6,0x40,0x68,0x37,0xBF,0x51,0xF5
806+
};
807+
PUTN(p256G, sizeof(p256G));
808+
}
809+
WRAP(bs, 0x03);
810+
}
811+
WRAP(s, 0x30);
812+
813+
/* extensions [3] */
814+
{
815+
size_t exts_outer = pos, exts_seq = pos, ext = pos, ev;
816+
unsigned char akid_oid[] = {0x06,0x03,0x55,0x1D,0x23};
817+
PUTN(akid_oid, sizeof(akid_oid));
818+
ev = pos;
819+
if (akid_val != NULL)
820+
PUTN(akid_val, akid_val_len);
821+
WRAP(ev, 0x04);
822+
WRAP(ext, 0x30);
823+
WRAP(exts_seq, 0x30);
824+
WRAP(exts_outer, 0xA3);
825+
}
826+
827+
WRAP(tbs_start, 0x30);
828+
829+
/* signatureAlgorithm */
830+
s = pos;
831+
{
832+
unsigned char oid[] = {0x06,0x08,0x2A,0x86,0x48,0xCE,
833+
0x3D,0x04,0x03,0x02};
834+
PUTN(oid, sizeof(oid));
835+
}
836+
WRAP(s, 0x30);
837+
838+
/* signatureValue: dummy */
839+
s = pos;
840+
{
841+
size_t sig;
842+
PUT1(0x00);
843+
sig = pos;
844+
PUT1(0x02); PUT1(0x01); PUT1(0x01);
845+
PUT1(0x02); PUT1(0x01); PUT1(0x01);
846+
WRAP(sig, 0x30);
847+
}
848+
WRAP(s, 0x03);
849+
850+
WRAP(0, 0x30); /* outer Certificate SEQUENCE */
851+
}
852+
853+
/* Parse the crafted certificate */
854+
x = wolfSSL_X509_d2i(NULL, buf, (int)pos);
855+
ExpectNotNull(x);
856+
857+
/* Attempt re-encode via i2d_X509_bio -- must fail gracefully, not
858+
* overflow. Before the fix this would write ~4000 bytes past the
859+
* end of cert->akid[]. */
860+
bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem());
861+
ExpectNotNull(bio);
862+
ExpectIntEQ(wolfSSL_i2d_X509_bio(bio, x), 0);
863+
864+
wolfSSL_BIO_free(bio);
865+
wolfSSL_X509_free(x);
866+
XFREE(akid_val, NULL, DYNAMIC_TYPE_TMP_BUFFER);
867+
#ifdef WOLFSSL_SMALL_STACK
868+
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
869+
#endif
870+
871+
#undef PUT1
872+
#undef PUTN
873+
#undef TLV_HDR
874+
#undef WRAP
875+
#endif
876+
return EXPECT_RESULT();
877+
}

tests/api/test_x509.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ int test_x509_GetCAByAKID(void);
2727
int test_x509_set_serialNumber(void);
2828
int test_x509_verify_cert_hostname_check(void);
2929
int test_x509_time_field_overread_via_tls(void);
30+
int test_x509_CertFromX509_akid_overflow(void);
3031

3132
#define TEST_X509_DECLS \
3233
TEST_DECL_GROUP("x509", test_x509_rfc2818_verification_callback), \
3334
TEST_DECL_GROUP("x509", test_x509_GetCAByAKID), \
3435
TEST_DECL_GROUP("x509", test_x509_set_serialNumber), \
3536
TEST_DECL_GROUP("x509", test_x509_verify_cert_hostname_check), \
36-
TEST_DECL_GROUP("x509", test_x509_time_field_overread_via_tls)
37+
TEST_DECL_GROUP("x509", test_x509_time_field_overread_via_tls), \
38+
TEST_DECL_GROUP("x509", test_x509_CertFromX509_akid_overflow)
3739

3840
#endif /* WOLFCRYPT_TEST_X509_H */

0 commit comments

Comments
 (0)