Skip to content

Commit 22b552c

Browse files
authored
Merge pull request #10809 from aidangarske/fenrir-6558-nameconstraints-minmax
Reject name constraint subtree with non-zero minimum or maximum
2 parents fed8d6a + 2124a10 commit 22b552c

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
@@ -23187,6 +23187,47 @@ static word32 build_simple_nameConstraints(byte* out, word32 outSz,
2318723187
XMEMCPY(out + 8, val, vlen);
2318823188
return n1 + 2;
2318923189
}
23190+
23191+
/* Build a NameConstraints extension value with a single subtree ([0]
23192+
* permitted or [1] excluded) whose GeneralSubtree carries a base GeneralName
23193+
* of context tag `gnTag` plus optional minimum ([0]) and maximum ([1])
23194+
* BaseDistance fields. Used to confirm that a non-zero minimum or any
23195+
* maximum is rejected (RFC 5280 4.2.1.10 requires minimum 0, maximum absent
23196+
* within this profile). */
23197+
static word32 build_minmax_nameConstraints(byte* out, word32 outSz,
23198+
int excluded, byte gnTag, const char* val, int minPresent, byte minVal,
23199+
int maxPresent, byte maxVal)
23200+
{
23201+
word32 vlen = (word32)XSTRLEN(val);
23202+
word32 extra = (word32)((minPresent ? 3 : 0) + (maxPresent ? 3 : 0));
23203+
word32 n3 = vlen + 2 + extra; /* GeneralSubtree content: base GN + min/max */
23204+
word32 n2 = n3 + 2; /* subtrees list content: one GeneralSubtree */
23205+
word32 n1 = n2 + 2; /* SEQUENCE content: the subtrees list */
23206+
word32 idx;
23207+
if (vlen > 0x7F || n3 > 0x7F || outSz < n1 + 2)
23208+
return 0;
23209+
out[0] = 0x30; /* SEQUENCE */
23210+
out[1] = (byte)n1;
23211+
out[2] = excluded ? 0xA1 : 0xA0; /* [1] excluded / [0] permitted */
23212+
out[3] = (byte)n2;
23213+
out[4] = 0x30; /* GeneralSubtree */
23214+
out[5] = (byte)n3;
23215+
out[6] = gnTag; /* base GeneralName */
23216+
out[7] = (byte)vlen;
23217+
XMEMCPY(out + 8, val, vlen);
23218+
idx = 8 + vlen;
23219+
if (minPresent) {
23220+
out[idx++] = 0x80; /* [0] minimum BaseDistance */
23221+
out[idx++] = 0x01;
23222+
out[idx++] = minVal;
23223+
}
23224+
if (maxPresent) {
23225+
out[idx++] = 0x81; /* [1] maximum BaseDistance */
23226+
out[idx++] = 0x01;
23227+
out[idx++] = maxVal;
23228+
}
23229+
return n1 + 2;
23230+
}
2319023231
#endif
2319123232

2319223233
/* End-to-end enforcement of DNS and URI nameConstraints against wildcard and
@@ -23348,6 +23389,60 @@ static int test_NameConstraints_DnsUriWildcard(void)
2334823389
return EXPECT_RESULT();
2334923390
}
2335023391

23392+
/* A GeneralSubtree's minimum/maximum BaseDistance fields were parsed but
23393+
* never stored or checked, so a subtree carrying a non-zero minimum or any
23394+
* maximum was silently accepted and then enforced as if it were minimum 0 /
23395+
* maximum absent. RFC 5280 4.2.1.10 requires minimum 0 and maximum absent
23396+
* within this profile, so such a constraint must be rejected. The bad
23397+
* constraint rides on the issuing CA, so a rejection surfaces as a failure
23398+
* to build the chain rather than a specific leaf error code. */
23399+
static int test_NameConstraints_SubtreeMinMax(void)
23400+
{
23401+
EXPECT_DECLS;
23402+
#if defined(WOLFSSL_ASN_TEMPLATE) && \
23403+
defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
23404+
defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
23405+
defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
23406+
defined(WOLFSSL_ALT_NAMES) && defined(WOLFSSL_CUSTOM_OID) && \
23407+
defined(HAVE_OID_ENCODING) && !defined(IGNORE_NAME_CONSTRAINTS)
23408+
byte nc[64];
23409+
word32 ncSz;
23410+
const byte DNS = 0x82; /* [2] dnsName */
23411+
23412+
/* (1) Control: minimum and maximum both absent -> conformant, accepts. */
23413+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 1, DNS, "example.com",
23414+
0, 0, 0, 0);
23415+
ExpectIntGT((int)ncSz, 0);
23416+
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23417+
23418+
/* (2) Control: explicit minimum == 0 is conformant -> accepts. Pins the
23419+
* rejection below to a non-zero minimum, not to any minimum field. */
23420+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 1, DNS, "example.com",
23421+
1, 0, 0, 0);
23422+
ExpectIntGT((int)ncSz, 0);
23423+
ExpectIntEQ(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23424+
23425+
/* (3) minimum == 1 must be rejected. */
23426+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 1, DNS, "example.com",
23427+
1, 1, 0, 0);
23428+
ExpectIntGT((int)ncSz, 0);
23429+
ExpectIntNE(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23430+
23431+
/* (4) maximum present (== 0) must be rejected. */
23432+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 1, DNS, "example.com",
23433+
0, 0, 1, 0);
23434+
ExpectIntGT((int)ncSz, 0);
23435+
ExpectIntNE(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23436+
23437+
/* (5) maximum present (== 5) must be rejected. */
23438+
ncSz = build_minmax_nameConstraints(nc, sizeof(nc), 0, DNS, "example.com",
23439+
0, 0, 1, 5);
23440+
ExpectIntGT((int)ncSz, 0);
23441+
ExpectIntNE(verify_with_otherName_chain(nc, ncSz, 1, NULL, 0), 0);
23442+
#endif
23443+
return EXPECT_RESULT();
23444+
}
23445+
2335123446
static int test_MakeCertWithCaFalse(void)
2335223447
{
2335323448
EXPECT_DECLS;
@@ -34967,6 +35062,7 @@ TEST_CASE testCases[] = {
3496735062
TEST_DECL(test_PathLenNoKeyUsage),
3496835063
TEST_DECL(test_NameConstraints_OtherName),
3496935064
TEST_DECL(test_NameConstraints_DnsUriWildcard),
35065+
TEST_DECL(test_NameConstraints_SubtreeMinMax),
3497035066
TEST_DECL(test_ParseSerial0FixtureMatrix),
3497135067
TEST_DECL(test_MakeCertWithCaFalse),
3497235068
#ifdef WOLFSSL_CERT_SIGN_CB

wolfcrypt/src/asn.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20537,7 +20537,20 @@ static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head,
2053720537
ret = GetASN_Items(subTreeASN, dataASN, subTreeASN_Length, 0, input,
2053820538
&idx, sz);
2053920539
if (ret == 0) {
20540-
byte t = dataASN[SUBTREEASN_IDX_BASE].tag;
20540+
byte t;
20541+
20542+
/* RFC 5280 Sec. 4.2.1.10: within this profile minimum must be 0
20543+
* and maximum must be absent. Reject a subtree that carries a
20544+
* non-zero minimum or any maximum rather than enforcing it as if
20545+
* those fields were the defaults. */
20546+
if ((minVal != 0) ||
20547+
(dataASN[SUBTREEASN_IDX_MAX].length > 0)) {
20548+
WOLFSSL_MSG("unsupported name constraint minimum/maximum");
20549+
ret = ASN_NAME_INVALID_E;
20550+
break;
20551+
}
20552+
20553+
t = dataASN[SUBTREEASN_IDX_BASE].tag;
2054120554

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

0 commit comments

Comments
 (0)