@@ -13272,6 +13272,78 @@ static int MatchIPv6(const char* pattern, int patternLen,
1327213272}
1327313273#endif /* WOLFSSL_IP_ALT_NAME && !WOLFSSL_USER_IO */
1327413274
13275+ /* Returns 1 if name is a syntactically valid DNS FQDN per RFC 952/1123.
13276+ *
13277+ * Rules enforced:
13278+ * - Total effective length (excluding optional trailing dot) in [1, 253]
13279+ * - Each label is 1-63 octets of [a-zA-Z0-9-]
13280+ * - No label starts or ends with '-'
13281+ * - At least two labels (single-label names are not "fully qualified")
13282+ * - Final label (TLD) contains at least one letter (rejects all-numeric
13283+ * strings that could be confused with IPv4 literals, and matches the
13284+ * ICANN constraint that TLDs are alphabetic)
13285+ * - Optional trailing dot is accepted (absolute FQDN form)
13286+ * - Internationalized names are valid in their ACE/punycode (xn--) form
13287+ */
13288+ static int IsValidFQDN(const char* name, word32 nameSz)
13289+ {
13290+ word32 i;
13291+ int labelLen = 0;
13292+ int labelCount = 0;
13293+ int tldHasAlpha = 0; /* tracks alpha presence in the *current* label */
13294+
13295+ if (name == NULL || nameSz <= 0)
13296+ return 0;
13297+
13298+ /* Strip a single optional trailing dot before measuring. "example.com."
13299+ * is the absolute form of the same FQDN.
13300+ */
13301+ if (name[nameSz - 1] == '.')
13302+ --nameSz;
13303+
13304+ if (nameSz < 1 || nameSz > 253)
13305+ return 0;
13306+
13307+ for (i = 0; i < nameSz; i++) {
13308+ byte c = (byte)name[i];
13309+
13310+ if (c == '.') {
13311+ if (labelLen == 0 || name[i - 1] == '-')
13312+ return 0; /* empty label or label ending in '-' */
13313+ ++labelCount;
13314+ labelLen = 0;
13315+ tldHasAlpha = 0; /* reset for next label */
13316+ continue;
13317+ }
13318+
13319+ if (++labelLen > 63)
13320+ return 0;
13321+
13322+ if (c == '-') {
13323+ if (labelLen == 1) /* label starts with '-' */
13324+ return 0;
13325+ }
13326+ else if (((c | 0x20) >= 'a') && ((c | 0x20) <= 'z')) {
13327+ tldHasAlpha = 1;
13328+ }
13329+ else if ((c < '0') || (c > '9')) {
13330+ return 0; /* character outside [a-zA-Z0-9-] */
13331+ }
13332+ }
13333+
13334+ /* Final label (no trailing dot in the effective range to close it) */
13335+ if ((labelLen == 0) || (name[nameSz - 1] == '-'))
13336+ return 0;
13337+ ++labelCount;
13338+
13339+ /* Technically, "Fully qualified" requires at least two labels, and the TLD
13340+ * must not be purely numeric (no such TLD exists; also rejects bare IPv4).
13341+ * We enforce the not-purely-numeric rule, but we allow single labels, so
13342+ * that ad hoc "localhost" CN is allowed.
13343+ */
13344+ return ((labelCount > 0) && tldHasAlpha);
13345+ }
13346+
1327513347/* Match names with wildcards, each wildcard can represent a single name
1327613348 component or fragment but not multiple names, i.e.,
1327713349 *.z.com matches y.z.com but not x.y.z.com
@@ -13296,6 +13368,19 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
1329613368 return 1;
1329713369#endif
1329813370
13371+ if (! IsValidFQDN(str, strLen)) {
13372+ /* Not a valid FQDN or IPv6 address -- require byte-exact match, no case
13373+ * folding, no wildcard interpretation. This is appropriate for an IPv4
13374+ * match, for example.
13375+ */
13376+ return (((word32)patternLen == strLen) &&
13377+ (XMEMCMP(pattern, str, patternLen) == 0));
13378+ }
13379+
13380+ /* strip trailing dot if necessary (FQDN designator). */
13381+ if (str[strLen-1] == '.')
13382+ --strLen;
13383+
1329913384 while (patternLen > 0) {
1330013385 /* Get the next pattern char to evaluate */
1330113386 char p = (char)XTOLOWER((unsigned char)*pattern);
0 commit comments