From d86174cc50dc267960a1b6bcf9927e44028970d2 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 6 May 2026 21:40:33 -0500 Subject: [PATCH] src/ssl.c: in wolfSSL_check_domain_name(), use XSTRCMP(), not strcmp(); wolfcrypt/src/asn.c, wolfssl/wolfcrypt/asn.h, src/ssl.c, wolfssl/ssl.h: move wolfssl_local_IsValidFQDN() from ASN.1 layer (where it has no users and is gated out in lean PSK builds) to TLS layer (where its users are); scripts/crl-revoked.test: use `cp --symbolic-link` opportunistically but fall back to `cp -p`. --- scripts/crl-revoked.test | 4 ++- src/ssl.c | 74 ++++++++++++++++++++++++++++++++++++++- wolfcrypt/src/asn.c | 75 ---------------------------------------- wolfssl/ssl.h | 2 ++ wolfssl/wolfcrypt/asn.h | 5 --- 5 files changed, 78 insertions(+), 82 deletions(-) diff --git a/scripts/crl-revoked.test b/scripts/crl-revoked.test index 18c0d2a582..92cc9ce14e 100755 --- a/scripts/crl-revoked.test +++ b/scripts/crl-revoked.test @@ -85,7 +85,9 @@ trap restore_file_system EXIT TMP_DIR=$(mktemp -d) || exit $? SRC_DIR="$PWD" pushd "$TMP_DIR" || exit $? -cp -r --symbolic-link "${SRC_DIR}/certs" . || exit $? +if ! cp -R --symbolic-link "${SRC_DIR}/certs" . 2>/dev/null; then + cp -pR "${SRC_DIR}/certs" . || exit $? +fi popd || exit $? CERT_DIR="${TMP_DIR}/certs" diff --git a/src/ssl.c b/src/ssl.c index bb28444bc9..6cc94778a0 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -7712,6 +7712,78 @@ int wolfSSL_Cleanup(void) return ret; } +/* Returns 1 if name is a syntactically valid DNS FQDN per RFC 952/1123. + * + * Rules enforced: + * - Total effective length (excluding optional trailing dot) in [1, 253] + * - Each label is 1-63 octets of [a-zA-Z0-9-], with _ allowed in all but + * the last label. + * - No label starts or ends with '-' + * - At least two labels (single-label names are not "fully qualified") + * - Final label (TLD) contains at least one letter (rejects all-numeric + * strings that could be confused with IPv4 literals, and matches the + * ICANN constraint that TLDs are alphabetic) + * - Optional trailing dot is accepted (absolute FQDN form) + * - Internationalized names are valid in their ACE/punycode (xn--) form + */ +int wolfssl_local_IsValidFQDN(const char* name, word32 nameSz) +{ + word32 i; + int labelLen = 0; + int labelCount = 0; + int curLabelHasAlpha = 0; + int curLabelHasUnderscore = 0; + + if (name == NULL || nameSz == 0) + return 0; + + /* Strip a single optional trailing dot before measuring. "example.com." + * is the absolute form of the same FQDN. + */ + if (name[nameSz - 1] == '.') + --nameSz; + + if (nameSz < 1 || nameSz > 253) + return 0; + + for (i = 0; i < nameSz; i++) { + byte c = (byte)name[i]; + + if (c == '.') { + if (labelLen == 0 || name[i - 1] == '-') + return 0; + ++labelCount; + labelLen = 0; + curLabelHasAlpha = 0; + curLabelHasUnderscore = 0; + continue; + } + + if (++labelLen > 63) + return 0; + + if (c == '-') { + if (labelLen == 1) + return 0; + } + else if (((c | 0x20) >= 'a') && ((c | 0x20) <= 'z')) { + curLabelHasAlpha = 1; + } + else if (c == '_') { + curLabelHasUnderscore = 1; + } + else if ((c < '0') || (c > '9')) { + return 0; + } + } + + /* Final label (no trailing dot in the effective range to close it) */ + if ((labelLen == 0) || (name[nameSz - 1] == '-') || curLabelHasUnderscore) + return 0; + ++labelCount; + + return ((labelCount > 1) && curLabelHasAlpha); +} /* call before SSL_connect, if verifying will add name check to date check and signature check */ @@ -7730,7 +7802,7 @@ int wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn) dn_len = XSTRLEN(dn); if ((! wolfssl_local_IsValidFQDN(dn, (word32)dn_len)) && - (strcmp(dn, "localhost") != 0)) + (XSTRCMP(dn, "localhost") != 0)) { WOLFSSL_MSG("Bad function argument: fails wolfssl_local_IsValidFQDN"); return WOLFSSL_FAILURE; diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 3b416d110b..d935483538 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -18075,81 +18075,6 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert) #endif /* IGNORE_NAME_CONSTRAINTS */ -#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) -/* Returns 1 if name is a syntactically valid DNS FQDN per RFC 952/1123. - * - * Rules enforced: - * - Total effective length (excluding optional trailing dot) in [1, 253] - * - Each label is 1-63 octets of [a-zA-Z0-9-], with _ allowed in all but - * the last label. - * - No label starts or ends with '-' - * - At least two labels (single-label names are not "fully qualified") - * - Final label (TLD) contains at least one letter (rejects all-numeric - * strings that could be confused with IPv4 literals, and matches the - * ICANN constraint that TLDs are alphabetic) - * - Optional trailing dot is accepted (absolute FQDN form) - * - Internationalized names are valid in their ACE/punycode (xn--) form - */ -int wolfssl_local_IsValidFQDN(const char* name, word32 nameSz) -{ - word32 i; - int labelLen = 0; - int labelCount = 0; - int curLabelHasAlpha = 0; - int curLabelHasUnderscore = 0; - - if (name == NULL || nameSz == 0) - return 0; - - /* Strip a single optional trailing dot before measuring. "example.com." - * is the absolute form of the same FQDN. - */ - if (name[nameSz - 1] == '.') - --nameSz; - - if (nameSz < 1 || nameSz > 253) - return 0; - - for (i = 0; i < nameSz; i++) { - byte c = (byte)name[i]; - - if (c == '.') { - if (labelLen == 0 || name[i - 1] == '-') - return 0; - ++labelCount; - labelLen = 0; - curLabelHasAlpha = 0; - curLabelHasUnderscore = 0; - continue; - } - - if (++labelLen > 63) - return 0; - - if (c == '-') { - if (labelLen == 1) - return 0; - } - else if (((c | 0x20) >= 'a') && ((c | 0x20) <= 'z')) { - curLabelHasAlpha = 1; - } - else if (c == '_') { - curLabelHasUnderscore = 1; - } - else if ((c < '0') || (c > '9')) { - return 0; - } - } - - /* Final label (no trailing dot in the effective range to close it) */ - if ((labelLen == 0) || (name[nameSz - 1] == '-') || curLabelHasUnderscore) - return 0; - ++labelCount; - - return ((labelCount > 1) && curLabelHasAlpha); -} -#endif /* !WOLFCRYPT_ONLY && !NO_CERTS */ - #ifdef WOLFSSL_ASN_TEMPLATE #if defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI) /* ASN.1 template for OtherName of an X.509 certificate. diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 89c1fb331b..4dd5a6bd55 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -3395,6 +3395,8 @@ WOLFSSL_API int wolfSSL_get_chain_cert_pem(WOLFSSL_X509_CHAIN* chain, int idx, /* call before SSL_connect, if verifying will add name check to date check and signature check */ WOLFSSL_ABI WOLFSSL_API int wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn); +WOLFSSL_TEST_VIS int wolfssl_local_IsValidFQDN(const char* name, + word32 nameSz); /* call before SSL_connect, if verifying will add IP address check to date check and signature check */ WOLFSSL_ABI WOLFSSL_API int wolfSSL_check_ip_address(WOLFSSL* ssl, const char* ipaddr); diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index aee161ec65..c0f77b6209 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -3179,11 +3179,6 @@ WOLFSSL_TEST_VIS int wolfssl_local_MatchIpSubnet(const byte* ip, int ipSz, int constraintSz); #endif -#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS) -WOLFSSL_TEST_VIS int wolfssl_local_IsValidFQDN(const char* name, - word32 nameSz); -#endif - #if ((defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)) \ || (defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT)) \ || (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)) \