Skip to content

Reject partial-label wildcard names by default (RFC 9525). - #12119

Open
kareem-wolfssl wants to merge 4 commits into
wolfSSL:masterfrom
kareem-wolfssl:zd22737
Open

kareem-wolfssl wants to merge 4 commits into
wolfSSL:masterfrom
kareem-wolfssl:zd22737

Conversation

@kareem-wolfssl

Copy link
Copy Markdown
Contributor

Description

Implement X509_CHECK_FLAG_NO_WILDCARDS and NO_PARTIAL_WILDCARDS.
Honor the host flags in the handshake peer-name check.

Fixes zd#22737

Testing

Built in + added tests, provided reproducer

Checklist

  • added tests
  • updated/added doxygen
  • updated appropriate READMEs
  • Updated manual and documentation

This matches OpenSSL's behavior for these flags and allows an easy
way to opt out of partial wildcard matching.
Previously, the host flags set by X509_VERIFY_PARAM_set_hostflags()
were being ignored by the time they reached X509_verify_cert(), because
ProcessPeerCert was passing in a literal 0 for these flags.
Now, the host flags are passed in if set.
RFC 6125 sec. 6.4.3 rule 3 said a client MAY match a presented identifier
whose wildcard is not the only character of the left-most label, so
"foo*.example.com" matched "foobar.example.com" and "foo.example.com".
RFC 9525, which obsoletes RFC 6125, requires the wildcard to be the complete
content of the left-most label and says a presented identifier that is not
MUST be ignored (sec. 6.3, and Appendix A lists this as a change).

Apply WOLFSSL_NO_PARTIAL_WILDCARDS by default. Define
WOLFSSL_ALLOW_PARTIAL_WILDCARDS to keep the RFC 6125 behavior.
Callers may still set the flag explicitly in either build.

Thanks to Han Qiu and Shuaigangran Yue (Southeast University) for
reporting this issue.
@kareem-wolfssl

Copy link
Copy Markdown
Contributor Author

@claude review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The set1_host() verification paths still ignore configured host flags, allowing wildcard certificates that callers explicitly forbid.

1 open issue
What changed in this PR

This PR tightens wolfSSL certificate hostname matching to reject partial-label wildcards by default under RFC 9525, while retaining an opt-in compatibility mode.

Changes:

  • Implements wildcard restriction flags and forwards host flags in the domain-name handshake check.
  • Updates hostname-matching tests for strict defaults and compatibility behavior.
  • Moves legacy partial-wildcard handshake cases into a conditional test suite.
File Description
tests/​test.conf Removes partial-wildcard cases from the default suite.
tests/​test-wildcard-partial.conf Adds compatibility-only partial-wildcard cases.
tests/​suites.c Runs the compatibility suite when partial wildcards are enabled.
tests/​include.am Includes the new test configuration in distributions.
tests/​api/​test_ossl_x509.c Tests wildcard restrictions and adjusts default expectations.
src/​x509.c Enables supported wildcard flags and corrects an unsupported-flag diagnostic.
src/​internal.c Enforces wildcard restrictions and forwards handshake host flags.

Review effort: Balanced

Comment thread src/internal.c
Comment on lines +21229 to +21230
unsigned int hostFlags = (ssl->param != NULL) ?
ssl->param->hostFlags : 0;
@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report · Explore what changed →

gcc-arm-cortex-m3

  • FLASH: .text +64 B (+0.0%, 132,213 B / 262,144 B, total: 50% used)

gcc-arm-cortex-m4

  • FLASH: .text +64 B (+0.0%, 209,548 B / 262,144 B, total: 80% used)

gcc-arm-cortex-m4-dtls13

  • FLASH: .text +64 B (+0.0%, 197,224 B / 1,048,576 B, total: 19% used)

gcc-arm-cortex-m4-openssl-compat

  • FLASH: .text +128 B (+0.0%, 804,332 B / 1,048,576 B, total: 77% used)

gcc-arm-cortex-m4-pq

  • FLASH: .text +64 B (+0.0%, 314,136 B / 1,048,576 B, total: 30% used)

gcc-arm-cortex-m4-rsa-only

  • FLASH: .text +64 B (+0.0%, 344,464 B / 1,048,576 B, total: 33% used)

gcc-arm-cortex-m4-tls12

  • FLASH: .text +64 B (+0.0%, 132,945 B / 262,144 B, total: 51% used)

gcc-arm-cortex-m4-tls13

  • FLASH: .text +64 B (+0.0%, 250,862 B / 262,144 B, total: 96% used)

gcc-arm-cortex-m7

  • FLASH: .text +64 B (+0.0%, 209,548 B / 262,144 B, total: 80% used)

gcc-arm-cortex-m7-pq

  • FLASH: .text +64 B (+0.0%, 314,968 B / 1,048,576 B, total: 30% used)

gcc-arm-cortex-m7-tls13

  • FLASH: .text +64 B (+0.0%, 250,862 B / 262,144 B, total: 96% used)

zephyr-frdm_rw612

  • FLASH: datas +16 B, log_const_area -8 B, rodata +36 B, text +11,624 B (+4.1%, 294,132 B / 67,108,864 B, total: 0% used)
  • RAM: bss +49 B, datas +16 B (+0.1%, 58,423 B / 983,040 B, total: 6% used)

No memory changes detected for:


Catch memory regressions in CI with MemBrowse. Free for open source.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟡 src/internal.c — nit: The header comment on WildcardPlacementOK still says partial wildcards like "a*" and "ab" keep their old matching, which is now wrong in default builds. The function now rejects them whenever WOLFSSL_NO_PARTIAL_WILDCARDS is set, and MatchDomainName sets that flag unless WOLFSSL_ALLOW_PARTIAL_WILDCARDS is defined. Fix: rewrite the comment to say partial-label wildcards are refused under WOLFSSL_NO_PARTIAL_WILDCARDS, which is the default, and are only exempt from the two-label rule when WOLFSSL_ALLOW_PARTIAL_WILDCARDS is defined.

    Why this was flagged

    The comment at src/internal.c:15447-15450 says "Partial left-most wildcards such as "a*" or "ab" retain their existing matching behavior". The new check at src/internal.c:15481-15483 returns 0 for those patterns when WOLFSSL_NO_PARTIAL_WILDCARDS is set. MatchDomainName ORs that flag in at src/internal.c:15565-15571 unless WOLFSSL_ALLOW_PARTIAL_WILDCARDS is defined, so the flag is set in every default build. Someone maintaining this security-sensitive hostname matcher, going by the comment, would assume partial wildcards still match by default, the opposite of what the code does. The code itself behaves correctly; only the documentation is wrong.

    Verification: nit. The trigger is any default build, i.e. one without WOLFSSL_ALLOW_PARTIAL_WILDCARDS. The diff leaves the WildcardPlacementOK header comment unchanged at src/internal.c:15447-15449. The check added at src/internal.c:15481-15483 returns 0 for "a*" and "ab". MatchDomainName sets that flag unconditionally at src/internal.c:15565-15571. Runtime behavior is unaffected.

  • 🟣 src/internal.c — Host flags are still ignored for names set with X509_VERIFY_PARAM_set1_host, so a WOLFSSL_NO_WILDCARDS app still accepts a "*.example.com" certificate. CheckPeerHostName at src/internal.c:19663 passes flags 0 to CheckForAltNames and to both MatchDomainName CN calls. ProcessPeerCerts at :21298 calls it with ssl->param->hostName, the usual OpenSSL path. Fix: pass ssl->param->hostFlags into CheckPeerHostName and use it at all three match calls, like the wolfSSL_check_domain_name path this PR updated.

    Why this was flagged

    The trigger is an OPENSSL_EXTRA app that pairs X509_VERIFY_PARAM_set1_host (or SSL_set1_host) with X509_VERIFY_PARAM_set_hostflags(WOLFSSL_NO_WILDCARDS), which is the standard OpenSSL idiom. In the handshake, ProcessPeerCerts (src/internal.c:21295-21298) checks ssl->param->hostName through CheckPeerHostName. That function hard-codes flags 0 in CheckForAltNames (src/internal.c:19663) and in MatchDomainName (src/internal.c:19669, 19678). A peer certificate with the SAN "*.example.com" therefore still matches "foo.example.com", even though the app asked for no wildcards. This PR changed only the wolfSSL_check_domain_name block (src/internal.c:21227-21280) to read hostFlags, and its stated goal is to honor host flags in the handshake peer-name check. The behavior on the verify-param path is pre-existing; this path was left as it was on the base branch. The x509_str.c:1396 path that does honor hostFlags runs only in wolfSSL_X509_verify_cert, not in this handshake check.

    Verification: Pre-existing: the base also ignores the flags on this path. ProcessPeerCerts checks the name at src/internal.c:21295-21299 with CheckPeerHostName(args->dCert, ssl->param->hostName). The flags are never passed in. CheckPeerHostName hard-codes flags 0 in all four match calls (src/internal.c:19663, 19669-19670, 19678-19679, 19682). Result: a SAN "*.example.com" still passes for "www.example.com" despite NO_WILDCARDS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants