From b6de2d3cbc3e999a912db7918e73cff28ee93205 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 6 May 2026 18:29:27 -0500 Subject: [PATCH 1/2] src/ssl.c: in wolfSSL_check_domain_name(), call wolfssl_local_IsValidFQDN() to validate the argument, with allowance for "localhost". scripts/crl-revoked.test: improve "Workaround to not pollute the certs folder" (don't copy whole source tree, and don't copy file contents). --- scripts/crl-revoked.test | 27 ++++++++++++++------------- src/ssl.c | 11 +++++++++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/scripts/crl-revoked.test b/scripts/crl-revoked.test index 99ea9b9b912..18c0d2a5825 100755 --- a/scripts/crl-revoked.test +++ b/scripts/crl-revoked.test @@ -29,11 +29,6 @@ elif [ "${AM_BWRAPPED-}" != "yes" ]; then unset AM_BWRAPPED fi -# Workaround to not pollute the certs folder with our files that can impact other tests -RUNNING_DIR=$(mktemp -d) -cp -rp . $RUNNING_DIR/. -cd $RUNNING_DIR - revocation_code="-361" revocation_code_openssl="23" exit_code=1 @@ -49,13 +44,9 @@ server_pid=$no_pid # also let's add some randomness by adding pid in case multiple 'make check's # per source tree ready_file=`pwd`/wolfssl_crl_ready$$ -CERT_DIR=certs remove_ready_file() { - if test -e "$ready_file"; then - echo -e "removing existing ready file" - rm "$ready_file" - fi + rm -f "$ready_file" } # trap this function so if user aborts with ^C or other kill signal we still @@ -84,10 +75,20 @@ trap abort_trap INT TERM # instead use "exit " and this function will run automatically restore_file_system() { remove_ready_file - cd / && rm -rf "$RUNNING_DIR" + if [ -n "$TMP_DIR" ]; then + rm -rf "$TMP_DIR" + fi } trap restore_file_system EXIT +# Workaround to not pollute the certs folder with our files that can impact other tests +TMP_DIR=$(mktemp -d) || exit $? +SRC_DIR="$PWD" +pushd "$TMP_DIR" || exit $? +cp -r --symbolic-link "${SRC_DIR}/certs" . || exit $? +popd || exit $? +CERT_DIR="${TMP_DIR}/certs" + run_test() { echo -e "\nStarting example server for crl test...\n" @@ -121,7 +122,7 @@ run_test() { crl_port="$(cat "$ready_file")" # starts client on crl_port and captures the output from client - capture_out=$(./examples/client/client -p $crl_port 2>&1) + capture_out=$(cd "${CERT_DIR}/.." && "${SRC_DIR}/examples/client/client" -p $crl_port 2>&1) client_result=$? wait $server_pid @@ -187,7 +188,7 @@ run_hashdir_test() { crl_port="$(cat "$ready_file")" # starts client on crl_port and captures the output from client - capture_out=$(./examples/client/client -p $crl_port -9 2>&1) + capture_out=$(cd "${CERT_DIR}/.." && "${SRC_DIR}/examples/client/client" -p $crl_port -9 2>&1) client_result=$? wait $server_pid diff --git a/src/ssl.c b/src/ssl.c index bc7e6074c85..bb28444bc90 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -7718,6 +7718,8 @@ int wolfSSL_Cleanup(void) WOLFSSL_ABI int wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn) { + size_t dn_len; + WOLFSSL_ENTER("wolfSSL_check_domain_name"); if (ssl == NULL || dn == NULL) { @@ -7725,6 +7727,15 @@ int wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn) return WOLFSSL_FAILURE; } + dn_len = XSTRLEN(dn); + + if ((! wolfssl_local_IsValidFQDN(dn, (word32)dn_len)) && + (strcmp(dn, "localhost") != 0)) + { + WOLFSSL_MSG("Bad function argument: fails wolfssl_local_IsValidFQDN"); + return WOLFSSL_FAILURE; + } + if (ssl->buffers.domainName.buffer) XFREE(ssl->buffers.domainName.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN); From d86174cc50dc267960a1b6bcf9927e44028970d2 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 6 May 2026 21:40:33 -0500 Subject: [PATCH 2/2] 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 18c0d2a5825..92cc9ce14e7 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 bb28444bc90..6cc94778a0f 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 3b416d110bc..d9354835385 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 89c1fb331b2..4dd5a6bd556 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 aee161ec65b..c0f77b62092 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)) \