Skip to content

Commit 2124a10

Browse files
committed
F-6558 - Reject name constraint subtree with non-zero minimum or maximum
1 parent 18c9684 commit 2124a10

2 files changed

Lines changed: 110 additions & 1 deletion

File tree

tests/api.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23208,6 +23208,47 @@ static word32 build_simple_nameConstraints(byte* out, word32 outSz,
2320823208
XMEMCPY(out + 8, val, vlen);
2320923209
return n1 + 2;
2321023210
}
23211+
23212+
/* Build a NameConstraints extension value with a single subtree ([0]
23213+
* permitted or [1] excluded) whose GeneralSubtree carries a base GeneralName
23214+
* of context tag `gnTag` plus optional minimum ([0]) and maximum ([1])
23215+
* BaseDistance fields. Used to confirm that a non-zero minimum or any
23216+
* maximum is rejected (RFC 5280 4.2.1.10 requires minimum 0, maximum absent
23217+
* within this profile). */
23218+
static word32 build_minmax_nameConstraints(byte* out, word32 outSz,
23219+
int excluded, byte gnTag, const char* val, int minPresent, byte minVal,
23220+
int maxPresent, byte maxVal)
23221+
{
23222+
word32 vlen = (word32)XSTRLEN(val);
23223+
word32 extra = (word32)((minPresent ? 3 : 0) + (maxPresent ? 3 : 0));
23224+
word32 n3 = vlen + 2 + extra; /* GeneralSubtree content: base GN + min/max */
23225+
word32 n2 = n3 + 2; /* subtrees list content: one GeneralSubtree */
23226+
word32 n1 = n2 + 2; /* SEQUENCE content: the subtrees list */
23227+
word32 idx;
23228+
if (vlen > 0x7F || n3 > 0x7F || outSz < n1 + 2)
23229+
return 0;
23230+
out[0] = 0x30; /* SEQUENCE */
23231+
out[1] = (byte)n1;
23232+
out[2] = excluded ? 0xA1 : 0xA0; /* [1] excluded / [0] permitted */
23233+
out[3] = (byte)n2;
23234+
out[4] = 0x30; /* GeneralSubtree */
23235+
out[5] = (byte)n3;
23236+
out[6] = gnTag; /* base GeneralName */
23237+
out[7] = (byte)vlen;
23238+
XMEMCPY(out + 8, val, vlen);
23239+
idx = 8 + vlen;
23240+
if (minPresent) {
23241+
out[idx++] = 0x80; /* [0] minimum BaseDistance */
23242+
out[idx++] = 0x01;
23243+
out[idx++] = minVal;
23244+
}
23245+
if (maxPresent) {
23246+
out[idx++] = 0x81; /* [1] maximum BaseDistance */
23247+
out[idx++] = 0x01;
23248+
out[idx++] = maxVal;
23249+
}
23250+
return n1 + 2;
23251+
}
2321123252
#endif
2321223253

2321323254
/* End-to-end enforcement of DNS and URI nameConstraints against wildcard and
@@ -23329,6 +23370,60 @@ static int test_NameConstraints_DnsUriWildcard(void)
2332923370
return EXPECT_RESULT();
2333023371
}
2333123372

23373+
/* A GeneralSubtree's minimum/maximum BaseDistance fields were parsed but
23374+
* never stored or checked, so a subtree carrying a non-zero minimum or any
23375+
* maximum was silently accepted and then enforced as if it were minimum 0 /
23376+
* maximum absent. RFC 5280 4.2.1.10 requires minimum 0 and maximum absent
23377+
* within this profile, so such a constraint must be rejected. The bad
23378+
* constraint rides on the issuing CA, so a rejection surfaces as a failure
23379+
* to build the chain rather than a specific leaf error code. */
23380+
static int test_NameConstraints_SubtreeMinMax(void)
23381+
{
23382+
EXPECT_DECLS;
23383+
#if defined(WOLFSSL_ASN_TEMPLATE) && \
23384+
defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
23385+
defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
23386+
defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
23387+
defined(WOLFSSL_ALT_NAMES) && defined(WOLFSSL_CUSTOM_OID) && \
23388+
defined(HAVE_OID_ENCODING) && !defined(IGNORE_NAME_CONSTRAINTS)
23389+
byte nc[64];
23390+
word32 ncSz;
23391+
const byte DNS = 0x82; /* [2] dnsName */
23392+
23393+
/* (1) Control: minimum and maximum both absent -> conformant, accepts. */
23394+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 1, DNS, "example.com",
23395+
0, 0, 0, 0);
23396+
ExpectIntGT((int)ncSz, 0);
23397+
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23398+
23399+
/* (2) Control: explicit minimum == 0 is conformant -> accepts. Pins the
23400+
* rejection below to a non-zero minimum, not to any minimum field. */
23401+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 1, DNS, "example.com",
23402+
1, 0, 0, 0);
23403+
ExpectIntGT((int)ncSz, 0);
23404+
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23405+
23406+
/* (3) minimum == 1 must be rejected. */
23407+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 1, DNS, "example.com",
23408+
1, 1, 0, 0);
23409+
ExpectIntGT((int)ncSz, 0);
23410+
ExpectIntNE(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23411+
23412+
/* (4) maximum present (== 0) must be rejected. */
23413+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 1, DNS, "example.com",
23414+
0, 0, 1, 0);
23415+
ExpectIntGT((int)ncSz, 0);
23416+
ExpectIntNE(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23417+
23418+
/* (5) maximum present (== 5) must be rejected. */
23419+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 0, DNS, "example.com",
23420+
0, 0, 1, 5);
23421+
ExpectIntGT((int)ncSz, 0);
23422+
ExpectIntNE(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23423+
#endif
23424+
return EXPECT_RESULT();
23425+
}
23426+
2333223427
static int test_MakeCertWithCaFalse(void)
2333323428
{
2333423429
EXPECT_DECLS;
@@ -34948,6 +35043,7 @@ TEST_CASE testCases[] = {
3494835043
TEST_DECL(test_PathLenNoKeyUsage),
3494935044
TEST_DECL(test_NameConstraints_OtherName),
3495035045
TEST_DECL(test_NameConstraints_DnsUriWildcard),
35046+
TEST_DECL(test_NameConstraints_SubtreeMinMax),
3495135047
TEST_DECL(test_ParseSerial0FixtureMatrix),
3495235048
TEST_DECL(test_MakeCertWithCaFalse),
3495335049
#ifdef WOLFSSL_CERT_SIGN_CB

wolfcrypt/src/asn.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20362,7 +20362,20 @@ static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head,
2036220362
ret = GetASN_Items(subTreeASN, dataASN, subTreeASN_Length, 0, input,
2036320363
&idx, sz);
2036420364
if (ret == 0) {
20365-
byte t = dataASN[SUBTREEASN_IDX_BASE].tag;
20365+
byte t;
20366+
20367+
/* RFC 5280 Sec. 4.2.1.10: within this profile minimum must be 0
20368+
* and maximum must be absent. Reject a subtree that carries a
20369+
* non-zero minimum or any maximum rather than enforcing it as if
20370+
* those fields were the defaults. */
20371+
if ((minVal != 0) ||
20372+
(dataASN[SUBTREEASN_IDX_MAX].length > 0)) {
20373+
WOLFSSL_MSG("unsupported name constraint minimum/maximum");
20374+
ret = ASN_NAME_INVALID_E;
20375+
break;
20376+
}
20377+
20378+
t = dataASN[SUBTREEASN_IDX_BASE].tag;
2036620379

2036720380
/* Check GeneralName tag is one of the types we can handle.
2036820381
* registeredID is included so that ConfirmNameConstraints can

0 commit comments

Comments
 (0)